diff --git a/common/src/main/java/com/scottlogic/deg/common/profile/constraints/atomic/StandardConstraintTypes.java b/common/src/main/java/com/scottlogic/deg/common/profile/constraints/atomic/StandardConstraintTypes.java index e56c50b23..3014c3b78 100644 --- a/common/src/main/java/com/scottlogic/deg/common/profile/constraints/atomic/StandardConstraintTypes.java +++ b/common/src/main/java/com/scottlogic/deg/common/profile/constraints/atomic/StandardConstraintTypes.java @@ -17,8 +17,19 @@ package com.scottlogic.deg.common.profile.constraints.atomic; public enum StandardConstraintTypes{ - SEDOL, - ISIN, - CUSIP, - RIC + SEDOL("[B-DF-HJ-NP-TV-Z0-9]{6}[0-9]"), + ISIN("[A-Z]{2}[A-Z0-9]{10}"), + CUSIP("[0-9]{3}[0-9A-Z]{5}[0-9]"), + RIC("[A-Z]{1,4}\\.[A-Z]{1,2}"); + + private final String regex; + + StandardConstraintTypes(String regex) { + + this.regex = regex; + } + + public String getRegex() { + return regex; + } } diff --git a/generator/src/main/java/com/scottlogic/deg/generator/fieldspecs/FieldSpecFactory.java b/generator/src/main/java/com/scottlogic/deg/generator/fieldspecs/FieldSpecFactory.java index 0b2805196..ccc63a025 100644 --- a/generator/src/main/java/com/scottlogic/deg/generator/fieldspecs/FieldSpecFactory.java +++ b/generator/src/main/java/com/scottlogic/deg/generator/fieldspecs/FieldSpecFactory.java @@ -30,8 +30,9 @@ import java.util.List; import java.util.regex.Pattern; +import static com.scottlogic.deg.common.profile.constraints.atomic.StandardConstraintTypes.RIC; + public class FieldSpecFactory { - public static final String RIC_REGEX = "[A-Z]{1,4}\\.[A-Z]{1,2}"; private final StringRestrictionsFactory stringRestrictionsFactory; @Inject @@ -256,12 +257,16 @@ private FieldSpec construct(ContainsRegexConstraint constraint, boolean negate) } private FieldSpec construct(MatchesStandardConstraint constraint, boolean negate) { - if (constraint.standard.equals(StandardConstraintTypes.RIC)) { - return construct(new MatchesRegexConstraint(constraint.field, Pattern.compile(RIC_REGEX)), negate); + if (constraint.standard.equals(RIC)) { + return construct(new MatchesRegexConstraint(constraint.field, Pattern.compile(RIC.getRegex())), negate); + } + + if (negate){ + return construct(new MatchesRegexConstraint(constraint.field, Pattern.compile(constraint.standard.getRegex())), negate); } return FieldSpec.Empty - .withStringRestrictions(new MatchesStandardStringRestrictions(constraint.standard, negate)); + .withStringRestrictions(new MatchesStandardStringRestrictions(constraint.standard)); } private FieldSpec construct(FormatConstraint constraint, boolean negate) { diff --git a/generator/src/main/java/com/scottlogic/deg/generator/generation/StandardFieldValueSourceEvaluator.java b/generator/src/main/java/com/scottlogic/deg/generator/generation/StandardFieldValueSourceEvaluator.java index 7f003b58d..d669dc71a 100644 --- a/generator/src/main/java/com/scottlogic/deg/generator/generation/StandardFieldValueSourceEvaluator.java +++ b/generator/src/main/java/com/scottlogic/deg/generator/generation/StandardFieldValueSourceEvaluator.java @@ -24,8 +24,8 @@ import com.scottlogic.deg.generator.generation.fieldvaluesources.FieldValueSource; import com.scottlogic.deg.generator.generation.fieldvaluesources.RealNumberFieldValueSource; import com.scottlogic.deg.generator.generation.fieldvaluesources.datetime.DateTimeFieldValueSource; -import com.scottlogic.deg.generator.generation.string.RegexStringGenerator; -import com.scottlogic.deg.generator.generation.string.StringGenerator; +import com.scottlogic.deg.generator.generation.string.generators.RegexStringGenerator; +import com.scottlogic.deg.generator.generation.string.generators.StringGenerator; import com.scottlogic.deg.generator.restrictions.*; import java.util.*; diff --git a/generator/src/main/java/com/scottlogic/deg/generator/generation/UpfrontTreePruner.java b/generator/src/main/java/com/scottlogic/deg/generator/generation/UpfrontTreePruner.java index e39240713..b242ca7cf 100644 --- a/generator/src/main/java/com/scottlogic/deg/generator/generation/UpfrontTreePruner.java +++ b/generator/src/main/java/com/scottlogic/deg/generator/generation/UpfrontTreePruner.java @@ -55,7 +55,6 @@ public DecisionTree runUpfrontPrune(DecisionTree tree, DataGeneratorMonitor moni monitor.addLineToPrintAtEndOfGeneration("The provided profile is wholly contradictory!"); monitor.addLineToPrintAtEndOfGeneration("No data can be generated!"); return new DecisionTree(null, tree.getFields()); - } else if (isPartiallyContradictory(markedTree.getRootNode())) { monitor.addLineToPrintAtEndOfGeneration(""); monitor.addLineToPrintAtEndOfGeneration("The provided profile is partially contradictory!"); diff --git a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/ChecksummedCodeStringGenerator.java b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/ChecksummedCodeStringGenerator.java deleted file mode 100644 index ff9962490..000000000 --- a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/ChecksummedCodeStringGenerator.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2019 Scott Logic Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.scottlogic.deg.generator.generation.string; - -import com.scottlogic.deg.generator.utils.*; - -import java.util.Arrays; -import java.util.Objects; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -public abstract class ChecksummedCodeStringGenerator implements StringGenerator { - protected final StringGenerator regexGenerator; - protected final boolean negate; - protected final int prefixLength; - protected final int codeLength; - - public ChecksummedCodeStringGenerator( - String generationPattern, - int codeLength, - int prefixLength - ) { - this( - new RegexStringGenerator(generationPattern, true), - false, - codeLength, - prefixLength - ); - } - - public ChecksummedCodeStringGenerator( - String generationPattern, - RegexStringGenerator intersectingGenerator, - int codeLength, - int prefixLength - ) { - this( - new RegexStringGenerator(generationPattern, true) - .intersect(intersectingGenerator), - false, - codeLength, - prefixLength - ); - } - - public ChecksummedCodeStringGenerator( - StringGenerator generator, - boolean negate, - int codeLength, - int prefixLength - ) { - this.negate = negate; - regexGenerator = generator; - this.codeLength = codeLength; - this.prefixLength = prefixLength; - } - - public abstract char calculateCheckDigit(String str); - - public abstract int getLength(); - - public String fixCheckDigit(String str) { - char checkDigit = calculateCheckDigit(str); - int codeLength = getLength(); - if (str.length() > prefixLength + codeLength) { - return str.substring(0, prefixLength + codeLength - 1) + - checkDigit + str.substring(prefixLength + codeLength); - } - return str.substring(0, str.length() - 1) + checkDigit; - } - - @Override - public StringGenerator intersect(StringGenerator stringGenerator) { - if (stringGenerator instanceof ChecksummedCodeStringGenerator) { - ChecksummedCodeStringGenerator otherGenerator = - (ChecksummedCodeStringGenerator)stringGenerator; - return intersect(otherGenerator.negate ? - otherGenerator.regexGenerator.complement() : - otherGenerator.regexGenerator); - } - if (stringGenerator instanceof RegexStringGenerator) { - return intersect((RegexStringGenerator)stringGenerator); - } - return new NoStringsStringGenerator( - RegexStringGenerator.intersectRepresentation(stringGenerator.toString(), "") - ); - } - - private StringGenerator intersect(RegexStringGenerator other) { - StringGenerator intersection = other.intersect(negate ? regexGenerator.complement() : regexGenerator); - if (!intersection.generateAllValues().iterator().hasNext() || !(intersection instanceof RegexStringGenerator)) { - return new NoStringsStringGenerator( - RegexStringGenerator.intersectRepresentation(other.toString(), regexGenerator.toString()) - ); - } - return instantiate((RegexStringGenerator)intersection); - } - - abstract ChecksummedCodeStringGenerator instantiate(RegexStringGenerator generator); - - @Override - public Iterable generateInterestingValues() { - if (negate) { - return new ConcatenatingIterable<>( - regexGenerator.complement().generateInterestingValues(), - generateInvalidCheckDigitStrings(regexGenerator::generateInterestingValues)); - } - return wrapIterableWithProjectionAndFilter(regexGenerator.generateInterestingValues()); - } - - @Override - public Iterable generateAllValues() { - if (negate) { - return new ConcatenatingIterable<>( - generateAllInvalidRegexStrings(), - generateAllInvalidCheckDigitStrings()); - } - return new FilteringIterable<>( - regexGenerator.generateAllValues(), - (x) -> x.equals(fixCheckDigit(x))); - } - - @Override - public Iterable generateRandomValues(RandomNumberGenerator randomNumberGenerator) { - if (negate) { - return new RandomMergingIterable<>( - Arrays.asList( - generateRandomInvalidRegexStrings(randomNumberGenerator), - generateRandomInvalidCheckDigitStrings(randomNumberGenerator)), - randomNumberGenerator); - } - return wrapIterableWithProjectionAndFilter( - regexGenerator.generateRandomValues(randomNumberGenerator) - ); - } - - private Iterable wrapIterableWithProjectionAndFilter(Iterable iterable) { - return IterableUtils.wrapIterableWithProjectionAndFilter( - iterable, - this::fixCheckDigit, - regexGenerator::match - ); - } - - private Iterable generateAllInvalidRegexStrings() { - return IterableUtils.wrapIterableWithNonEmptyStringCheck( - regexGenerator.complement().generateAllValues() - ); - } - - private Iterable generateRandomInvalidRegexStrings(RandomNumberGenerator randomNumberGenerator) { - return IterableUtils.wrapIterableWithNonEmptyStringCheck( - regexGenerator.complement().generateRandomValues(randomNumberGenerator) - ); - } - - private Iterable generateAllInvalidCheckDigitStrings() { - return generateInvalidCheckDigitStrings(regexGenerator::generateAllValues); - } - - private Iterable generateRandomInvalidCheckDigitStrings(RandomNumberGenerator randomNumberGenerator) { - return generateInvalidCheckDigitStrings( - () -> regexGenerator.generateRandomValues(randomNumberGenerator)); - } - - private Iterable generateInvalidCheckDigitStrings(Supplier> valueSupplier) { - return new FlatteningIterable<>( - valueSupplier.get(), - initialValue -> { - String sansCheckDigit = initialValue.substring( - prefixLength, - prefixLength + codeLength - 1 - ); - final char checkDigit = calculateCheckDigit(sansCheckDigit); - return IntStream.range(0, 10).boxed() - .map(digit -> Character.forDigit(digit, 10)) - .filter(digit -> digit != checkDigit) - .map(digit -> sansCheckDigit + digit) - .collect(Collectors.toList()); - }); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - ChecksummedCodeStringGenerator that = (ChecksummedCodeStringGenerator) o; - return negate == that.negate; - } - - @Override - public int hashCode() { - return Objects.hash(negate, getClass()); - } -} diff --git a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/CusipStringGenerator.java b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/CusipStringGenerator.java deleted file mode 100644 index c2a16f923..000000000 --- a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/CusipStringGenerator.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2019 Scott Logic Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.scottlogic.deg.generator.generation.string; - -import com.scottlogic.deg.generator.utils.*; - -public class CusipStringGenerator extends ChecksummedCodeStringGenerator { - public final static int CUSIP_LENGTH = 9; - public final static String STANDARD_REGEX_REPRESENTATION = "[0-9]{3}[0-9A-Z]{5}[0-9]"; - - public CusipStringGenerator() { - super(STANDARD_REGEX_REPRESENTATION, CUSIP_LENGTH, 0); - } - - public CusipStringGenerator(String prefix, String suffix, RegexStringGenerator additionalRestrictions) { - super( - prefix + STANDARD_REGEX_REPRESENTATION + suffix, - additionalRestrictions, - CUSIP_LENGTH, - prefix.length() - ); - } - - private CusipStringGenerator(RegexStringGenerator generator) { - super(generator, false, CUSIP_LENGTH, 0); - } - - private CusipStringGenerator(StringGenerator cusipGenerator, boolean negate) { - super(cusipGenerator, negate, CUSIP_LENGTH, 0); - } - - @Override - public char calculateCheckDigit(String str) { - return FinancialCodeUtils.calculateCusipCheckDigit( - str.substring(prefixLength, CUSIP_LENGTH + prefixLength - 1) - ); - } - - @Override - public int getLength() { - return CUSIP_LENGTH; - } - - @Override - public StringGenerator complement() { - return new CusipStringGenerator(regexGenerator, !negate); - } - - @Override - public boolean match(String subject) { - boolean matches = FinancialCodeUtils.isValidCusipNsin(subject); - return matches != negate; - } - - @Override - ChecksummedCodeStringGenerator instantiate(RegexStringGenerator generator) { - return new CusipStringGenerator(generator); - } -} diff --git a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/IsinStringGenerator.java b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/IsinStringGenerator.java deleted file mode 100644 index 1296b0e24..000000000 --- a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/IsinStringGenerator.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright 2019 Scott Logic Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.scottlogic.deg.generator.generation.string; - -import com.scottlogic.deg.generator.utils.*; - -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class IsinStringGenerator implements StringGenerator { - public static final int ISIN_LENGTH = 12; - private static final String GENERIC_NSIN_REGEX = "[A-Z0-9]{9}"; - - // This generator is not used in generation itself, but is used to describe the possible - // range of output values when combining with other string generators. - private RegexStringGenerator isinRegexGenerator; - - public IsinStringGenerator() { - this(getRegexGeneratorForAllLegalIsinFormats()); - } - - IsinStringGenerator(RegexStringGenerator regexGenerator) { - isinRegexGenerator = regexGenerator; - } - - @Override - public StringGenerator intersect(StringGenerator stringGenerator) { - if (stringGenerator instanceof IsinStringGenerator) { - return - new IsinStringGenerator((RegexStringGenerator)isinRegexGenerator - .intersect(((IsinStringGenerator) stringGenerator).isinRegexGenerator)); - } - if (stringGenerator instanceof NegatedIsinGenerator) { - return new NoStringsStringGenerator( - RegexStringGenerator.intersectRepresentation(stringGenerator.toString(), "") - ); - } - if (stringGenerator instanceof ChecksummedCodeStringGenerator) { - // Assume that no other checksummed string format we know about is going to be - // compatible with the ISIN format. This is true at the time of writing. - return new NoStringsStringGenerator( - RegexStringGenerator.intersectRepresentation(stringGenerator.toString(), "") - ); - } - if (stringGenerator instanceof RegexStringGenerator) { - return intersect((RegexStringGenerator)stringGenerator); - } - return new NoStringsStringGenerator( - RegexStringGenerator.intersectRepresentation(stringGenerator.toString(), "") - ); - } - - private StringGenerator intersect(RegexStringGenerator other) { - StringGenerator intersection = - other.intersect(isinRegexGenerator); - if (!(intersection instanceof RegexStringGenerator)) { - return new NoStringsStringGenerator( - RegexStringGenerator.intersectRepresentation( - other.toString(), - isinRegexGenerator.toString() - ) - ); - } - return new IsinStringGenerator((RegexStringGenerator)intersection); - } - - @Override - public StringGenerator complement() { - return new NegatedIsinGenerator(isinRegexGenerator); - } - - @Override - public boolean match(String subject) { - return FinancialCodeUtils.isValidIsin(subject); - } - - @Override - public Iterable generateInterestingValues() { - final List> countryCodeIterables = getAllCountryIsinGeneratorsAsStream() - .limit(2) - .map(generator -> IterableUtils.wrapIterableWithProjectionAndFilter( - generator.generateInterestingValues(), - this::replaceCheckDigit, - isinRegexGenerator::match)) - .collect(Collectors.toList()); - return new ConcatenatingIterable<>(countryCodeIterables); - } - - @Override - public Iterable generateAllValues() { - final List> countryCodeIterables = getAllCountryIsinGeneratorsAsStream() - .map(generator -> new FilteringIterable<>( - generator.generateAllValues(), - (x) -> x.equals(replaceCheckDigit(x)))) - .collect(Collectors.toList()); - return new ConcatenatingIterable<>(countryCodeIterables); - } - - @Override - public Iterable generateRandomValues(RandomNumberGenerator randomNumberGenerator) { - final List> countryCodeIterables = getAllCountryIsinGeneratorsAsStream() - .map(generator -> IterableUtils.wrapIterableWithProjectionAndFilter( - generator.generateRandomValues(randomNumberGenerator), - this::replaceCheckDigit, - isinRegexGenerator::match - )) - .collect(Collectors.toList()); - return new RandomMergingIterable<>(countryCodeIterables, randomNumberGenerator); - } - - private String replaceCheckDigit(String isin) { - String isinWithoutCheckDigit = isin.substring(0, isin.length() - 1); - return isinWithoutCheckDigit + FinancialCodeUtils.calculateIsinCheckDigit(isinWithoutCheckDigit); - } - - private Stream getAllCountryIsinGeneratorsAsStream() { - return FinancialCodeUtils.VALID_COUNTRY_CODES.stream() - .map(this::getIsinGeneratorForCountry); - } - - private StringGenerator getIsinGeneratorForCountry(String countryCode) { - if (countryCode.equals("GB")) { - return new SedolStringGenerator("GB00", "[0-9]", isinRegexGenerator); - } - if (countryCode.equals("US")) { - return new CusipStringGenerator("US", "[0-9]", isinRegexGenerator); - } - return new RegexStringGenerator(countryCode + GENERIC_NSIN_REGEX + "[0-9]", true); - } - - private static RegexStringGenerator getRegexGeneratorForAllLegalIsinFormats() { - Stream countryGenerators = FinancialCodeUtils.VALID_COUNTRY_CODES - .stream() - .map( - country -> new RegexStringGenerator(getIsinRegexRepresentationForCountry(country), true) - ); - RegexStringGenerator.UnionCollector collector = countryGenerators.collect( - RegexStringGenerator.UnionCollector::new, - RegexStringGenerator.UnionCollector::accumulate, - RegexStringGenerator.UnionCollector::combine - ); - return collector.getUnionGenerator(); - } - - private static String getIsinRegexRepresentationForCountrySansCheckDigit(String countryCode) { - if (countryCode.equals("GB")) { - return "GB00" + SedolStringGenerator.STANDARD_REGEX_REPRESENTATION; - } - else if (countryCode.equals("US")) { - return "US" + CusipStringGenerator.STANDARD_REGEX_REPRESENTATION; - } - else { - return countryCode + GENERIC_NSIN_REGEX; - } - } - - private static String getIsinRegexRepresentationForCountry(String countryCode) { - return getIsinRegexRepresentationForCountrySansCheckDigit(countryCode) + "[0-9]"; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - IsinStringGenerator that = (IsinStringGenerator) o; - return Objects.equals(this.isinRegexGenerator, that.isinRegexGenerator); - } - - @Override - public int hashCode() { - return Objects.hash(isinRegexGenerator); - } -} diff --git a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/NegatedIsinGenerator.java b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/NegatedIsinGenerator.java deleted file mode 100644 index 0cc57534c..000000000 --- a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/NegatedIsinGenerator.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright 2019 Scott Logic Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.scottlogic.deg.generator.generation.string; - -import com.scottlogic.deg.generator.utils.*; - -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -public class NegatedIsinGenerator implements StringGenerator { - private static final String GENERIC_NSIN_REGEX = "[A-Z0-9]{9}"; - - // This generator is not used in generation itself, but is used to describe the possible - // range of output values when combining with other string generators. - private RegexStringGenerator isinRegexGenerator; - private StringGenerator notIsinRegexGenerator; - - - public NegatedIsinGenerator(RegexStringGenerator isinRegexGenerator) { - this.isinRegexGenerator = isinRegexGenerator; - notIsinRegexGenerator = isinRegexGenerator.complement(); - } - - @Override - public StringGenerator intersect(StringGenerator stringGenerator) { - if (stringGenerator instanceof NegatedIsinGenerator) { - RegexStringGenerator otherRegexGenerator = - ((NegatedIsinGenerator) stringGenerator).isinRegexGenerator; - return new NegatedIsinGenerator(isinRegexGenerator.union(otherRegexGenerator)); - } - if (stringGenerator instanceof IsinStringGenerator) { - return new NoStringsStringGenerator( - RegexStringGenerator.intersectRepresentation(stringGenerator.toString(), "") - ); - } - if (stringGenerator instanceof ChecksummedCodeStringGenerator) { - return stringGenerator; - } - if (stringGenerator instanceof RegexStringGenerator) { - return stringGenerator.intersect(notIsinRegexGenerator); - } - return new NoStringsStringGenerator( - RegexStringGenerator.intersectRepresentation(stringGenerator.toString(), "") - ); - } - - @Override - public StringGenerator complement() { - return new IsinStringGenerator(isinRegexGenerator); - } - - @Override - public boolean match(String subject) { - return !FinancialCodeUtils.isValidIsin(subject); - } - - @Override - public Iterable generateInterestingValues() { - return new ConcatenatingIterable<>( - Arrays.asList( - generateInterestingInvalidCountryStrings(), - generateInterestingCountriesWithInvalidNsins(), - generateInterestingInvalidCheckDigitIsins())); - } - - @Override - public Iterable generateAllValues() { - return new ConcatenatingIterable<>( - Arrays.asList( - generateAllInvalidCountryStrings(), - generateAllCountriesWithInvalidNsins(), - generateAllInvalidCheckDigitIsins())); - } - - @Override - public Iterable generateRandomValues(RandomNumberGenerator randomNumberGenerator) { - return new RandomMergingIterable<>( - Arrays.asList( - generateRandomInvalidCountryStrings(randomNumberGenerator), - generateRandomCountriesWithInvalidNsins(randomNumberGenerator), - generateRandomInvalidCheckDigitIsins(randomNumberGenerator)), - randomNumberGenerator); - } - - private Iterable generateInterestingInvalidCountryStrings() { - final String invalidCountryCodeRegex = FinancialCodeUtils.VALID_COUNTRY_CODES.stream() - .limit(2) - .collect(Collectors.joining("|", "((?!", ")).*")); - return new RegexStringGenerator(invalidCountryCodeRegex, true).generateInterestingValues(); - } - - private Iterable generateAllInvalidCountryStrings() { - final String invalidCountryCodeRegex = FinancialCodeUtils.VALID_COUNTRY_CODES.stream() - .collect(Collectors.joining("|", "((?!", ")).*")); - return new RegexStringGenerator(invalidCountryCodeRegex, true).generateAllValues(); - } - - private Iterable generateRandomInvalidCountryStrings(RandomNumberGenerator randomNumberGenerator) { - final String invalidCountryCodeRegex = FinancialCodeUtils.VALID_COUNTRY_CODES.stream() - .collect(Collectors.joining("|", "((?!", ")).*")); - return new RegexStringGenerator(invalidCountryCodeRegex, true).generateRandomValues(randomNumberGenerator); - } - - private static Iterable generateInterestingCountriesWithInvalidNsins() { - final List> countryWithInvalidNsinIterables = FinancialCodeUtils.VALID_COUNTRY_CODES.stream() - .limit(2) - .map(countryCode -> { - final StringGenerator nsinGeneratorForCountry = getNsinGeneratorForCountry(countryCode); - final Iterable invalidNsinIterators = nsinGeneratorForCountry.complement().generateInterestingValues(); - return new ProjectingIterable<>(invalidNsinIterators, invalidNsin -> countryCode + invalidNsin); - }) - .collect(Collectors.toList()); - return new FilteringIterable<>(new ConcatenatingIterable<>(countryWithInvalidNsinIterables), - isin -> !FinancialCodeUtils.isValidIsin(isin)); - } - - private static Iterable generateAllCountriesWithInvalidNsins() { - final List> countryWithInvalidNsinIterables = FinancialCodeUtils.VALID_COUNTRY_CODES.stream() - .map(countryCode -> { - final StringGenerator nsinGeneratorForCountry = getNsinGeneratorForCountry(countryCode); - final Iterable invalidNsinIterators = nsinGeneratorForCountry.complement().generateAllValues(); - return new ProjectingIterable<>(invalidNsinIterators, invalidNsin -> countryCode + invalidNsin); - }) - .collect(Collectors.toList()); - return new FilteringIterable<>(new ConcatenatingIterable<>(countryWithInvalidNsinIterables), - isin -> !FinancialCodeUtils.isValidIsin(isin)); - } - - private static Iterable generateRandomCountriesWithInvalidNsins(RandomNumberGenerator randomNumberGenerator) { - final List> countryWithInvalidNsinIterables = FinancialCodeUtils.VALID_COUNTRY_CODES.stream() - .map(countryCode -> { - final StringGenerator nsinGeneratorForCountry = getNsinGeneratorForCountry(countryCode); - final Iterable invalidNsinIterators = nsinGeneratorForCountry.complement().generateRandomValues(randomNumberGenerator); - return new ProjectingIterable<>(invalidNsinIterators, invalidNsin -> countryCode + invalidNsin); - }) - .collect(Collectors.toList()); - return new FilteringIterable<>(new RandomMergingIterable<>(countryWithInvalidNsinIterables, randomNumberGenerator), - isin -> !FinancialCodeUtils.isValidIsin(isin)); - } - - private Iterable generateInterestingInvalidCheckDigitIsins() { - final List> countryCodeIterables = getAllCountryIsinGeneratorsAsStream() - .limit(2) - .map(isinSansCheckDigitGenerator -> - new FlatteningIterable<>( - isinSansCheckDigitGenerator.generateInterestingValues(), - isinSansCheckDigit -> { - final char checkDigit = FinancialCodeUtils.calculateIsinCheckDigit(isinSansCheckDigit); - return IntStream.range(0, 10).boxed() - .map(digit -> Character.forDigit(digit, 10)) - .filter(digit -> digit != checkDigit) - .map(digit -> isinSansCheckDigit + digit) - .collect(Collectors.toList()); - })) - .collect(Collectors.toList()); - return new ConcatenatingIterable<>(countryCodeIterables); - } - - private Iterable generateAllInvalidCheckDigitIsins() { - final List> countryCodeIterables = getAllCountryIsinGeneratorsAsStream() - .map(isinSansCheckDigitGenerator -> - new FlatteningIterable<>( - isinSansCheckDigitGenerator.generateAllValues(), - isinSansCheckDigit -> { - final char checkDigit = FinancialCodeUtils.calculateIsinCheckDigit(isinSansCheckDigit); - return IntStream.range(0, 10).boxed() - .map(digit -> Character.forDigit(digit, 10)) - .filter(digit -> digit != checkDigit) - .map(digit -> isinSansCheckDigit + digit) - .collect(Collectors.toList()); - })) - .collect(Collectors.toList()); - return new ConcatenatingIterable<>(countryCodeIterables); - } - - private Iterable generateRandomInvalidCheckDigitIsins(RandomNumberGenerator randomNumberGenerator) { - final List> countryCodeIterables = getAllCountryIsinGeneratorsAsStream() - .map(isinSansCheckDigitGenerator -> - new FlatteningIterable<>( - isinSansCheckDigitGenerator.generateRandomValues(randomNumberGenerator), - isinSansCheckDigit -> { - final char checkDigit = FinancialCodeUtils.calculateIsinCheckDigit(isinSansCheckDigit); - return IntStream.range(0, 10).boxed() - .map(digit -> Character.forDigit(digit, 10)) - .filter(digit -> digit != checkDigit) - .map(digit -> isinSansCheckDigit + digit) - .collect(Collectors.toList()); - })) - .collect(Collectors.toList()); - return new RandomMergingIterable<>(countryCodeIterables, randomNumberGenerator); - } - - private Stream getAllCountryIsinGeneratorsAsStream() { - return FinancialCodeUtils.VALID_COUNTRY_CODES.stream() - .map(this::getIsinGeneratorForCountry); - } - - private StringGenerator getIsinGeneratorForCountry(String countryCode) { - if (countryCode.equals("GB")) { - return new SedolStringGenerator("GB00", "[0-9]", isinRegexGenerator); - } - if (countryCode.equals("US")) { - return new CusipStringGenerator("US", "[0-9]", isinRegexGenerator); - } - return new RegexStringGenerator(countryCode + GENERIC_NSIN_REGEX + "[0-9]", true); - } - - private static StringGenerator getNsinGeneratorForCountry(String countryCode) { - if (countryCode.equals("GB")) { - return new SedolStringGenerator("00"); - } - if (countryCode.equals("US")) { - return new CusipStringGenerator(); - } - return new RegexStringGenerator(GENERIC_NSIN_REGEX, true); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - NegatedIsinGenerator that = (NegatedIsinGenerator) o; - return Objects.equals(this.isinRegexGenerator, that.isinRegexGenerator); - } - - @Override - public int hashCode() { - return Objects.hash(isinRegexGenerator); - } -} diff --git a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/SedolStringGenerator.java b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/SedolStringGenerator.java deleted file mode 100644 index aaf93646c..000000000 --- a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/SedolStringGenerator.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2019 Scott Logic Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.scottlogic.deg.generator.generation.string; - -import com.scottlogic.deg.generator.utils.*; - -public class SedolStringGenerator extends ChecksummedCodeStringGenerator { - public final static int SEDOL_LENGTH = 7; - public final static String STANDARD_REGEX_REPRESENTATION = "[B-DF-HJ-NP-TV-Z0-9]{6}[0-9]"; - - public SedolStringGenerator() { - super(STANDARD_REGEX_REPRESENTATION, SEDOL_LENGTH, 0); - } - - public SedolStringGenerator(String prefix) { this(prefix, ""); } - - public SedolStringGenerator(String prefix, String suffix) { - super(prefix + STANDARD_REGEX_REPRESENTATION + suffix, SEDOL_LENGTH, 0); - } - - public SedolStringGenerator(String prefix, String suffix, RegexStringGenerator additionalRestrictions) { - super(prefix + STANDARD_REGEX_REPRESENTATION + suffix, additionalRestrictions, SEDOL_LENGTH, prefix.length()); - } - - private SedolStringGenerator(RegexStringGenerator sedolGenerator) { - super(sedolGenerator, false, SEDOL_LENGTH, 0); - } - - private SedolStringGenerator(StringGenerator sedolGenerator, boolean negate) { - super(sedolGenerator, negate, SEDOL_LENGTH, 0); - } - - @Override - public char calculateCheckDigit(String str) { - return FinancialCodeUtils.calculateSedolCheckDigit( - str.substring(prefixLength, SEDOL_LENGTH + prefixLength - 1) - ); - } - - @Override - public int getLength() { - return SEDOL_LENGTH; - } - - @Override - public StringGenerator complement() { - return new SedolStringGenerator(regexGenerator, !negate); - } - - @Override - public boolean match(String subject) { - boolean matches = FinancialCodeUtils.isValidSedolNsin(subject, prefixLength); - return matches != negate; - } - - @Override - ChecksummedCodeStringGenerator instantiate(RegexStringGenerator generator) { - return new SedolStringGenerator(generator); - } -} diff --git a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumMaker.java b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumMaker.java new file mode 100644 index 000000000..6e5954de5 --- /dev/null +++ b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumMaker.java @@ -0,0 +1,21 @@ +/* + * Copyright 2019 Scott Logic Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.scottlogic.deg.generator.generation.string.generators; + +@FunctionalInterface +public interface ChecksumMaker { + Character makeChecksum(String input); +} diff --git a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumStringGenerator.java b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumStringGenerator.java new file mode 100644 index 000000000..0172bfbc4 --- /dev/null +++ b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumStringGenerator.java @@ -0,0 +1,73 @@ +/* + * Copyright 2019 Scott Logic Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.scottlogic.deg.generator.generation.string.generators; + +import com.scottlogic.deg.common.ValidationException; +import com.scottlogic.deg.generator.utils.RandomNumberGenerator; + +import java.util.function.Function; +import java.util.stream.Stream; + +public class ChecksumStringGenerator implements StringGenerator { + + private final StringGenerator checksumlessGenerator; + private final ChecksumMaker checksumMaker; + + public ChecksumStringGenerator(StringGenerator checksumlessGenerator, ChecksumMaker checksumMaker) { + this.checksumlessGenerator = checksumlessGenerator; + this.checksumMaker = checksumMaker; + } + + @Override + public Stream generateAllValues() { + return checksumlessGenerator.generateAllValues() + .map(addChecksum()); + } + + + @Override + public Stream generateRandomValues(RandomNumberGenerator randomNumberGenerator) { + return checksumlessGenerator.generateRandomValues(randomNumberGenerator) + .map(addChecksum()); + } + + @Override + public Stream generateInterestingValues() { + return checksumlessGenerator.generateInterestingValues() + .map(addChecksum()); + } + + private Function addChecksum() { + return string -> string + checksumMaker.makeChecksum(string); + } + + @Override + public boolean matches(String string) { + if (string.length() < 1) { + return false; + } + String preChecksumComponent = string.substring(0, string.length() - 1); + Character checksumComponent = string.charAt(string.length() - 1); + return + checksumlessGenerator.matches(preChecksumComponent) && + checksumMaker.makeChecksum(preChecksumComponent).equals(checksumComponent); + } + + @Override + public StringGenerator intersect(StringGenerator stringGenerator) { + throw new ValidationException("These constraints cannot be combined."); + } +} diff --git a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumStringGeneratorFactory.java b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumStringGeneratorFactory.java new file mode 100644 index 000000000..b96e8eb2b --- /dev/null +++ b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumStringGeneratorFactory.java @@ -0,0 +1,39 @@ +/* + * Copyright 2019 Scott Logic Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.scottlogic.deg.generator.generation.string.generators; + +import com.scottlogic.deg.generator.utils.FinancialCodeUtils; + +public class ChecksumStringGeneratorFactory { + + public static StringGenerator createSedolGenerator() { + return new ChecksumStringGenerator( + new RegexStringGenerator("[B-DF-HJ-NP-TV-Z0-9]{6}", true), + FinancialCodeUtils::calculateSedolCheckDigit); + } + + public static StringGenerator createCusipGenerator() { + return new ChecksumStringGenerator( + new RegexStringGenerator("[0-9]{3}[0-9A-Z]{5}", true), + FinancialCodeUtils::calculateCusipCheckDigit); + } + + public static StringGenerator createIsinGenerator() { + return new ChecksumStringGenerator( + new ChecksumlessIsinGenerator(), + FinancialCodeUtils::calculateIsinCheckDigit); + } +} diff --git a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumlessIsinGenerator.java b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumlessIsinGenerator.java new file mode 100644 index 000000000..59bc58142 --- /dev/null +++ b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumlessIsinGenerator.java @@ -0,0 +1,71 @@ +/* + * Copyright 2019 Scott Logic Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.scottlogic.deg.generator.generation.string.generators; + +import com.scottlogic.deg.common.ValidationException; +import com.scottlogic.deg.generator.utils.RandomNumberGenerator; + +import java.util.Arrays; +import java.util.stream.Stream; + +import static com.scottlogic.deg.common.util.FlatMappingSpliterator.flatMap; + +public class ChecksumlessIsinGenerator implements StringGenerator { + + @Override + public Stream generateAllValues() { + Stream isinStringGenerators = + Arrays.stream(IsinCountryCode.values()) + .map(IsinCountryCode::getChecksumlessStringGenerator); + + return flatMap( + isinStringGenerators, + StringGenerator::generateAllValues); + } + + @Override + public Stream generateRandomValues(RandomNumberGenerator randomNumberGenerator) { + return Stream.generate(() -> + getRandomCountryCode(randomNumberGenerator) + .getChecksumlessStringGenerator() + .generateRandomValues(randomNumberGenerator) + .findFirst().get()); + } + + private IsinCountryCode getRandomCountryCode(RandomNumberGenerator randomNumberGenerator) { + int random = randomNumberGenerator.nextInt(IsinCountryCode.values().length); + return IsinCountryCode.values()[random]; + } + + @Override + public Stream generateInterestingValues() { + return generateAllValues().limit(2); + } + + @Override + public boolean matches(String string) { + Stream isinStringGenerators = + Arrays.stream(IsinCountryCode.values()) + .map(IsinCountryCode::getChecksumlessStringGenerator); + + return isinStringGenerators.anyMatch(generator -> generator.matches(string)); + } + + @Override + public StringGenerator intersect(StringGenerator stringGenerator) { + throw new ValidationException("Constraints with ISINs can only be used with length and equalTo constraints."); + } +} diff --git a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/IsinCountryCode.java b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/IsinCountryCode.java new file mode 100644 index 000000000..3490c5802 --- /dev/null +++ b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/IsinCountryCode.java @@ -0,0 +1,292 @@ +/* + * Copyright 2019 Scott Logic Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.scottlogic.deg.generator.generation.string.generators; + + +import static com.scottlogic.deg.generator.generation.string.generators.ChecksumStringGeneratorFactory.createCusipGenerator; +import static com.scottlogic.deg.generator.generation.string.generators.ChecksumStringGeneratorFactory.createSedolGenerator; + +public enum IsinCountryCode { + AD, + AE, + AF, + AG, + AI, + AL, + AM, + AO, + AQ, + AR, + AS, + AT, + AU, + AW, + AX, + AZ, + BA, + BB, + BD, + BE, + BF, + BG, + BH, + BI, + BJ, + BL, + BM, + BN, + BO, + BQ, + BR, + BS, + BT, + BV, + BW, + BY, + BZ, + CA, + CC, + CD, + CF, + CG, + CH, + CI, + CK, + CL, + CM, + CN, + CO, + CR, + CU, + CV, + CW, + CX, + CY, + CZ, + DE, + DJ, + DK, + DM, + DO, + DZ, + EC, + EE, + EG, + EH, + ER, + ES, + ET, + FI, + FJ, + FK, + FM, + FO, + FR, + GA, + GB(prefix("GB00", createSedolGenerator())), + GD, + GE, + GF, + GG, + GH, + GI, + GL, + GM, + GN, + GP, + GQ, + GR, + GS, + GT, + GU, + GW, + GY, + HK, + HM, + HN, + HR, + HT, + HU, + ID, + IE, + IL, + IM, + IN, + IO, + IQ, + IR, + IS, + IT, + JE, + JM, + JO, + JP, + KE, + KG, + KH, + KI, + KM, + KN, + KP, + KR, + KW, + KY, + KZ, + LA, + LB, + LC, + LI, + LK, + LR, + LS, + LT, + LU, + LV, + LY, + MA, + MC, + MD, + ME, + MF, + MG, + MH, + MK, + ML, + MM, + MN, + MO, + MP, + MQ, + MR, + MS, + MT, + MU, + MV, + MW, + MX, + MY, + MZ, + NA, + NC, + NE, + NF, + NG, + NI, + NL, + NO, + NP, + NR, + NU, + NZ, + OM, + PA, + PE, + PF, + PG, + PH, + PK, + PL, + PM, + PN, + PR, + PS, + PT, + PW, + PY, + QA, + RE, + RO, + RS, + RU, + RW, + SA, + SB, + SC, + SD, + SE, + SG, + SH, + SI, + SJ, + SK, + SL, + SM, + SN, + SO, + SR, + SS, + ST, + SV, + SX, + SY, + SZ, + TC, + TD, + TF, + TG, + TH, + TJ, + TK, + TL, + TM, + TN, + TO, + TR, + TT, + TV, + TW, + TZ, + UA, + UG, + UM, + US(prefix("US", createCusipGenerator())), + UY, + UZ, + VA, + VC, + VE, + VG, + VI, + VN, + VU, + WF, + WS, + YE, + YT, + ZA, + ZM, + ZW; + + String GENERIC_NSIN_REGEX = "[A-Z0-9]{9}"; + private final StringGenerator checksumlessStringGenerator; + + IsinCountryCode(){ + checksumlessStringGenerator = prefix(this.name(), + new RegexStringGenerator(GENERIC_NSIN_REGEX, true)); + } + + IsinCountryCode(StringGenerator stringGenerator){ + this.checksumlessStringGenerator = stringGenerator; + } + + static StringGenerator prefix(String prefix, StringGenerator inner){ + return new PrefixingStringGenerator(prefix, inner); + } + + public StringGenerator getChecksumlessStringGenerator() { + return checksumlessStringGenerator; + } +} diff --git a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/NoStringsStringGenerator.java b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/NoStringsStringGenerator.java similarity index 60% rename from generator/src/main/java/com/scottlogic/deg/generator/generation/string/NoStringsStringGenerator.java rename to generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/NoStringsStringGenerator.java index 635821317..9e34011e0 100644 --- a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/NoStringsStringGenerator.java +++ b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/NoStringsStringGenerator.java @@ -14,12 +14,11 @@ * limitations under the License. */ -package com.scottlogic.deg.generator.generation.string; +package com.scottlogic.deg.generator.generation.string.generators; import com.scottlogic.deg.generator.utils.RandomNumberGenerator; -import java.util.Arrays; -import java.util.Collections; +import java.util.stream.Stream; public class NoStringsStringGenerator implements StringGenerator { private final String stringRepresentation; @@ -33,34 +32,27 @@ public String toString() { return String.format("No strings: %s", this.stringRepresentation); } - @Override - public StringGenerator intersect(StringGenerator stringGenerator) { - return new NoStringsStringGenerator( - RegexStringGenerator.intersectRepresentation(this.stringRepresentation, stringGenerator.toString())); - } - - @Override - public StringGenerator complement() { - throw new RuntimeException("Not implemented: Return a string generator able to emit ALL strings"); + public boolean matches(String subject) { + return false; } @Override - public boolean match(String subject) { - return false; + public StringGenerator intersect(StringGenerator stringGenerator) { + return this; } @Override - public Iterable generateInterestingValues() { - return Collections.emptySet(); + public Stream generateInterestingValues() { + return Stream.empty(); } @Override - public Iterable generateAllValues() { - return Collections.emptySet(); + public Stream generateAllValues() { + return Stream.empty(); } @Override - public Iterable generateRandomValues(RandomNumberGenerator randomNumberGenerator) { - return Collections.emptySet(); + public Stream generateRandomValues(RandomNumberGenerator randomNumberGenerator) { + return Stream.empty(); } } diff --git a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/PrefixingStringGenerator.java b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/PrefixingStringGenerator.java new file mode 100644 index 000000000..889b55ac5 --- /dev/null +++ b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/PrefixingStringGenerator.java @@ -0,0 +1,67 @@ +/* + * Copyright 2019 Scott Logic Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.scottlogic.deg.generator.generation.string.generators; + +import com.scottlogic.deg.common.ValidationException; +import com.scottlogic.deg.generator.utils.RandomNumberGenerator; + +import java.util.stream.Stream; + +public class PrefixingStringGenerator implements StringGenerator { + + private final String prefix; + private final StringGenerator innerGenerator; + + public PrefixingStringGenerator(String prefix, StringGenerator innerGenerator) { + this.prefix = prefix; + this.innerGenerator = innerGenerator; + } + + @Override + public Stream generateAllValues() { + return innerGenerator.generateAllValues() + .map(string -> prefix + string); + } + + @Override + public Stream generateRandomValues(RandomNumberGenerator randomNumberGenerator) { + return innerGenerator.generateRandomValues(randomNumberGenerator) + .map(string -> prefix + string); + } + + @Override + public Stream generateInterestingValues() { + return innerGenerator.generateInterestingValues() + .map(string -> prefix + string); + } + + @Override + public boolean matches(String string) { + if (string.length() < prefix.length()) { + return false; + } + String candidatePrefix = string.substring(0, prefix.length()); + if (!candidatePrefix.equals(prefix)) { + return false; + } + return innerGenerator.matches(string.substring(prefix.length())); + } + + @Override + public StringGenerator intersect(StringGenerator stringGenerator) { + throw new ValidationException("The prefixing generator cannot be combined "); + } +} diff --git a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/RegexStringGenerator.java b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/RegexStringGenerator.java similarity index 86% rename from generator/src/main/java/com/scottlogic/deg/generator/generation/string/RegexStringGenerator.java rename to generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/RegexStringGenerator.java index 90d7c4f5a..daafa0738 100644 --- a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/RegexStringGenerator.java +++ b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/RegexStringGenerator.java @@ -14,8 +14,9 @@ * limitations under the License. */ -package com.scottlogic.deg.generator.generation.string; +package com.scottlogic.deg.generator.generation.string.generators; +import com.scottlogic.deg.generator.generation.string.AutomatonUtils; import com.scottlogic.deg.generator.generation.string.iterators.FiniteStringAutomatonIterator; import com.scottlogic.deg.generator.generation.string.factorys.InterestingStringFactory; import com.scottlogic.deg.generator.generation.string.factorys.RandomStringFactory; @@ -24,8 +25,8 @@ import dk.brics.automaton.Automaton; import java.util.*; - - +import java.util.stream.Stream; +import java.util.stream.StreamSupport; public class RegexStringGenerator implements StringGenerator { @@ -101,7 +102,7 @@ public StringGenerator intersect(StringGenerator otherGenerator) { String mergedRepresentation = intersectRepresentation( this.regexRepresentation, - otherRegexGenerator.regexRepresentation); + ((RegexStringGenerator)otherGenerator).regexRepresentation); return new RegexStringGenerator(merged, mergedRepresentation); } @@ -136,31 +137,30 @@ static String unionRepresentation(String left, String right) { } @Override - public Iterable generateInterestingValues() { - return interestingStringFactory.generateInterestingValues(automaton); + public Stream generateInterestingValues() { + return StreamSupport.stream(interestingStringFactory.generateInterestingValues(automaton).spliterator(), false); } @Override - public Iterable generateAllValues() { - return () -> new FiniteStringAutomatonIterator(automaton); + public Stream generateAllValues() { + Iterator iterator = new FiniteStringAutomatonIterator(automaton); + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.SORTED), false); } @Override - public Iterable generateRandomValues(RandomNumberGenerator randomNumberGenerator) { - return () -> new SupplierBasedIterator<>( + public Stream generateRandomValues(RandomNumberGenerator randomNumberGenerator) { + final Iterable iterable = () -> new SupplierBasedIterator<>( () -> randomStringFactory.createRandomString( "", automaton.getInitialState(), 1, Integer.MAX_VALUE, randomNumberGenerator)); + return StreamSupport.stream(iterable.spliterator(), false); } - @Override - public boolean match(String subject) { - + public boolean matches(String subject) { return automaton.run(subject); - } public boolean equals(Object o) { diff --git a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/StringGenerator.java b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/StringGenerator.java similarity index 83% rename from generator/src/main/java/com/scottlogic/deg/generator/generation/string/StringGenerator.java rename to generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/StringGenerator.java index 545e29a5e..4072087e2 100644 --- a/generator/src/main/java/com/scottlogic/deg/generator/generation/string/StringGenerator.java +++ b/generator/src/main/java/com/scottlogic/deg/generator/generation/string/generators/StringGenerator.java @@ -13,24 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.scottlogic.deg.generator.generation.string; +package com.scottlogic.deg.generator.generation.string.generators; import com.scottlogic.deg.generator.generation.fieldvaluesources.FieldValueSource; import com.scottlogic.deg.generator.utils.RandomNumberGenerator; import com.scottlogic.deg.generator.utils.UpCastingIterator; +import java.util.stream.Stream; + public interface StringGenerator { - StringGenerator intersect(StringGenerator stringGenerator); - StringGenerator complement(); + Stream generateAllValues(); - boolean match(String subject); + Stream generateRandomValues(RandomNumberGenerator randomNumberGenerator); - Iterable generateInterestingValues(); + Stream generateInterestingValues(); - Iterable generateAllValues(); + boolean matches(String string); - Iterable generateRandomValues(RandomNumberGenerator randomNumberGenerator); + StringGenerator intersect(StringGenerator stringGenerator); + + default StringGenerator complement() { + throw new UnsupportedOperationException(); + } default FieldValueSource asFieldValueSource() { return new StringGeneratorAsFieldValueSource(this); diff --git a/generator/src/main/java/com/scottlogic/deg/generator/restrictions/MatchesStandardStringRestrictions.java b/generator/src/main/java/com/scottlogic/deg/generator/restrictions/MatchesStandardStringRestrictions.java index 6f9bd2414..cdf8133d3 100644 --- a/generator/src/main/java/com/scottlogic/deg/generator/restrictions/MatchesStandardStringRestrictions.java +++ b/generator/src/main/java/com/scottlogic/deg/generator/restrictions/MatchesStandardStringRestrictions.java @@ -16,11 +16,13 @@ package com.scottlogic.deg.generator.restrictions; +import com.scottlogic.deg.common.ValidationException; import com.scottlogic.deg.common.profile.constraints.atomic.StandardConstraintTypes; -import com.scottlogic.deg.generator.generation.string.CusipStringGenerator; -import com.scottlogic.deg.generator.generation.string.IsinStringGenerator; -import com.scottlogic.deg.generator.generation.string.SedolStringGenerator; -import com.scottlogic.deg.generator.generation.string.StringGenerator; +import com.scottlogic.deg.generator.generation.string.generators.NoStringsStringGenerator; +import com.scottlogic.deg.generator.generation.string.generators.RegexStringGenerator; +import com.scottlogic.deg.generator.generation.string.generators.StringGenerator; + +import static com.scottlogic.deg.generator.generation.string.generators.ChecksumStringGeneratorFactory.*; /** * Represents the restriction of a field to an `aValid` operator @@ -28,38 +30,28 @@ */ public class MatchesStandardStringRestrictions implements StringRestrictions{ private final StandardConstraintTypes type; - private final boolean negated; private StringGenerator generator; - public MatchesStandardStringRestrictions(StandardConstraintTypes type, boolean negated) { + public MatchesStandardStringRestrictions(StandardConstraintTypes type) { this.type = type; - this.negated = negated; } @Override public boolean match(String x) { - return createGenerator().match(x); + return createGenerator().matches(x); } public StringGenerator createGenerator() { - StringGenerator generator = getStringGenerator(); - - return negated - ? generator.complement() - : generator; - } - - private StringGenerator getStringGenerator() { if (generator == null) { switch (type) { case ISIN: - generator = new IsinStringGenerator(); + generator = createIsinGenerator(); break; case SEDOL: - generator = new SedolStringGenerator(); + generator = createSedolGenerator(); break; case CUSIP: - generator = new CusipStringGenerator(); + generator = createCusipGenerator(); break; default: throw new UnsupportedOperationException(String.format("Unable to create string generator for: %s", type)); @@ -68,34 +60,11 @@ private StringGenerator getStringGenerator() { return generator; } - /** - * Will combine/intersect another StringRestrictions within this instance - * - * Rules are: - * Return this instance within modification if the other restrictions matches the following: - * 1. It is an equivalent MatchesStandardStringRestrictions instance (all properties match) - * 2. It is a TextualRestrictions that has: - * 2.1. no regex restrictions of any kind - * 2.2. any present length restrictions do not impact the ability to create any value - * - * @param other The other restrictions to combine/intersect - * @return Either this instance (success) or a NoStringsPossibleStringRestrictions if the other restrictions could not be merged or intersected - */ @Override public MergeResult intersect(StringRestrictions other) { - if (other instanceof TextualRestrictions){ - TextualRestrictions textualRestrictions = (TextualRestrictions) other; - StringGenerator combinedGenerator = getCombinedGenerator(textualRestrictions); - if (!combinedGenerator.generateAllValues().iterator().hasNext()){ - return MergeResult.unsuccessful(); - } - - return new MergeResult<>(copyWithGenerator(combinedGenerator)); - } - - if (!(other instanceof MatchesStandardStringRestrictions)){ - return MergeResult.unsuccessful(); + if (other instanceof TextualRestrictions){ + return isLengthAcceptable((TextualRestrictions) other); } MatchesStandardStringRestrictions that = (MatchesStandardStringRestrictions) other; @@ -103,61 +72,29 @@ public MergeResult intersect(StringRestrictions other) { return MergeResult.unsuccessful(); } - return that.negated == negated - ? new MergeResult<>(this) - : MergeResult.unsuccessful(); + return new MergeResult<>(this); } - private StringGenerator getCombinedGenerator(TextualRestrictions textualRestrictions) { - StringGenerator ourGenerator = getStringGenerator(); - return ourGenerator.intersect(textualRestrictions.createGenerator()); - } + private MergeResult isLengthAcceptable(TextualRestrictions other) { + if (anyRegexes(other)){ + throw new ValidationException("Combining a regex constraint with a " + this.toString() + " constraint is not supported."); + } - private MatchesStandardStringRestrictions copyWithGenerator(StringGenerator generator) { - MatchesStandardStringRestrictions newRestrictions = - new MatchesStandardStringRestrictions(type, negated); - newRestrictions.generator = generator; - return newRestrictions; - } + StringGenerator intersect = other.createGenerator().intersect(new RegexStringGenerator(type.getRegex(), true)); - private enum Impact - { - /** - * There is definitely a partial impact on value production, but its level is - * currently unknown. - */ - PARTIAL, - - /** - * There is potentially no impact on the production of values - */ - NONE, - - /** - * The impact is such that values definitely cannot be produced. - */ - FULL + if (intersect instanceof NoStringsStringGenerator){ + return MergeResult.unsuccessful(); + } + + return new MergeResult<>(this); } - private int getCodeLength(StandardConstraintTypes type) { - switch (type) - { - case ISIN: - return IsinStringGenerator.ISIN_LENGTH; - case SEDOL: - return SedolStringGenerator.SEDOL_LENGTH; - case CUSIP: - return CusipStringGenerator.CUSIP_LENGTH; - default: - throw new UnsupportedOperationException(String.format("Unable to check string restrictions for: %s", type)); - } + private boolean anyRegexes(TextualRestrictions other) { + return !other.containingRegex.isEmpty() || !other.matchingRegex.isEmpty() || !other.notContainingRegex.isEmpty() || !other.notMatchingRegex.isEmpty(); } @Override public String toString() { - if (negated){ - return "not " + type.name(); - } return type.name(); } } diff --git a/generator/src/main/java/com/scottlogic/deg/generator/restrictions/StringRestrictions.java b/generator/src/main/java/com/scottlogic/deg/generator/restrictions/StringRestrictions.java index 8a330f4d8..ed293b3eb 100644 --- a/generator/src/main/java/com/scottlogic/deg/generator/restrictions/StringRestrictions.java +++ b/generator/src/main/java/com/scottlogic/deg/generator/restrictions/StringRestrictions.java @@ -17,7 +17,7 @@ package com.scottlogic.deg.generator.restrictions; import com.scottlogic.deg.common.profile.constraints.atomic.IsOfTypeConstraint; -import com.scottlogic.deg.generator.generation.string.StringGenerator; +import com.scottlogic.deg.generator.generation.string.generators.StringGenerator; public interface StringRestrictions extends TypedRestrictions { MergeResult intersect(StringRestrictions other); diff --git a/generator/src/main/java/com/scottlogic/deg/generator/restrictions/TextualRestrictions.java b/generator/src/main/java/com/scottlogic/deg/generator/restrictions/TextualRestrictions.java index 4c74e6aeb..10c5f0be1 100644 --- a/generator/src/main/java/com/scottlogic/deg/generator/restrictions/TextualRestrictions.java +++ b/generator/src/main/java/com/scottlogic/deg/generator/restrictions/TextualRestrictions.java @@ -16,9 +16,9 @@ package com.scottlogic.deg.generator.restrictions; -import com.scottlogic.deg.generator.generation.string.NoStringsStringGenerator; -import com.scottlogic.deg.generator.generation.string.RegexStringGenerator; -import com.scottlogic.deg.generator.generation.string.StringGenerator; +import com.scottlogic.deg.generator.generation.string.generators.NoStringsStringGenerator; +import com.scottlogic.deg.generator.generation.string.generators.RegexStringGenerator; +import com.scottlogic.deg.generator.generation.string.generators.StringGenerator; import com.scottlogic.deg.generator.utils.SetUtils; import java.util.*; @@ -57,7 +57,7 @@ public class TextualRestrictions implements StringRestrictions { @Override public boolean match(String x) { - return createGenerator().match(x); + return createGenerator().matches(x); } /** @@ -149,7 +149,7 @@ public boolean match(Object o) { String s = (String) o; StringGenerator generator = createGenerator(); - return generator == null || generator.match(s); + return generator == null || generator.matches(s); } /** diff --git a/generator/src/test/java/com/scottlogic/deg/generator/fieldspecs/FieldSpecTests.java b/generator/src/test/java/com/scottlogic/deg/generator/fieldspecs/FieldSpecTests.java index 246b6b4b4..0c17e83f4 100644 --- a/generator/src/test/java/com/scottlogic/deg/generator/fieldspecs/FieldSpecTests.java +++ b/generator/src/test/java/com/scottlogic/deg/generator/fieldspecs/FieldSpecTests.java @@ -20,7 +20,7 @@ import com.scottlogic.deg.common.profile.constraints.atomic.IsOfTypeConstraint.Types; import com.scottlogic.deg.generator.fieldspecs.whitelist.DistributedSet; import com.scottlogic.deg.generator.fieldspecs.whitelist.FrequencyDistributedSet; -import com.scottlogic.deg.generator.generation.string.StringGenerator; +import com.scottlogic.deg.generator.generation.string.generators.StringGenerator; import com.scottlogic.deg.generator.restrictions.*; import com.scottlogic.deg.generator.utils.SetUtils; import org.junit.Assert; @@ -32,7 +32,10 @@ import java.math.BigDecimal; import java.time.OffsetDateTime; import java.time.ZoneOffset; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import java.util.stream.Stream; import static org.hamcrest.core.IsEqual.equalTo; diff --git a/generator/src/test/java/com/scottlogic/deg/generator/generation/string/CusipStringGeneratorTests.java b/generator/src/test/java/com/scottlogic/deg/generator/generation/string/CusipStringGeneratorTests.java index 2f8edbd42..4f3eee347 100644 --- a/generator/src/test/java/com/scottlogic/deg/generator/generation/string/CusipStringGeneratorTests.java +++ b/generator/src/test/java/com/scottlogic/deg/generator/generation/string/CusipStringGeneratorTests.java @@ -16,45 +16,84 @@ package com.scottlogic.deg.generator.generation.string; -import org.junit.Assert; +import com.scottlogic.deg.generator.generation.string.generators.StringGenerator; +import com.scottlogic.deg.generator.utils.FinancialCodeUtils; +import com.scottlogic.deg.generator.utils.JavaUtilRandomNumberGenerator; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import java.util.Iterator; + +import static com.scottlogic.deg.generator.generation.string.generators.ChecksumStringGeneratorFactory.createCusipGenerator; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class CusipStringGeneratorTests { + @Test + public void shouldEndAllCusipsWithValidCheckDigit() { + StringGenerator target = createCusipGenerator(); + final int NumberOfTests = 100; + + final Iterator allCusips = target.generateAllValues().iterator(); + + for (int ii = 0; ii < NumberOfTests; ++ii) { + final String nextCusip = allCusips.next(); + final char checkDigit = FinancialCodeUtils.calculateCusipCheckDigit(nextCusip.substring(0, 8)); + assertThat(nextCusip.charAt(8), equalTo(checkDigit)); + } + } + + @Test + public void shouldEndAllRandomCusipsWithValidCheckDigit() { + StringGenerator target = createCusipGenerator(); + + final int NumberOfTests = 100; + + final Iterator allCusips = target.generateRandomValues(new JavaUtilRandomNumberGenerator()).iterator(); + + for (int ii = 0; ii < NumberOfTests; ++ii) { + final String nextCusip = allCusips.next(); + final char checkDigit = FinancialCodeUtils.calculateCusipCheckDigit(nextCusip.substring(0, 8)); + assertThat(nextCusip.charAt(8), equalTo(checkDigit)); + } + } + @Test public void shouldMatchAValidCusipCodeWhenNotNegated(){ - StringGenerator cusipGenerator = new CusipStringGenerator(); + StringGenerator cusipGenerator = createCusipGenerator(); - boolean matches = cusipGenerator.match("38259P508"); + boolean matches = cusipGenerator.matches("38259P508"); assertTrue(matches); } @Test public void shouldNotMatchAnInvalidCusipCodeWhenNotNegated(){ - StringGenerator cusipGenerator = new CusipStringGenerator(); + StringGenerator cusipGenerator = createCusipGenerator(); - boolean matches = cusipGenerator.match("not a cusip"); + boolean matches = cusipGenerator.matches("not a cusip"); assertFalse(matches); } @Test + @Disabled("Standard constraints e.g. ISINs currently cannot be negated") public void shouldNotMatchAValidCusipCodeWhenNegated(){ - StringGenerator cusipGenerator = new CusipStringGenerator().complement(); + StringGenerator cusipGenerator = createCusipGenerator().complement(); - boolean matches = cusipGenerator.match("38259P508"); + boolean matches = cusipGenerator.matches("38259P508"); assertFalse(matches); } @Test + @Disabled("Standard constraints e.g. ISINs currently cannot be negated") public void shouldMatchAnInvalidCusipCodeWhenNegated(){ - StringGenerator cusipGenerator = new CusipStringGenerator().complement(); + StringGenerator cusipGenerator = createCusipGenerator().complement(); - boolean matches = cusipGenerator.match("not a cusip"); + boolean matches = cusipGenerator.matches("not a cusip"); assertTrue(matches); } diff --git a/generator/src/test/java/com/scottlogic/deg/generator/generation/string/IsinStringGeneratorTests.java b/generator/src/test/java/com/scottlogic/deg/generator/generation/string/IsinStringGeneratorTests.java index 6a5126481..1365bf1ab 100644 --- a/generator/src/test/java/com/scottlogic/deg/generator/generation/string/IsinStringGeneratorTests.java +++ b/generator/src/test/java/com/scottlogic/deg/generator/generation/string/IsinStringGeneratorTests.java @@ -16,25 +16,28 @@ package com.scottlogic.deg.generator.generation.string; +import com.scottlogic.deg.generator.generation.string.generators.RegexStringGenerator; +import com.scottlogic.deg.generator.generation.string.generators.StringGenerator; import com.scottlogic.deg.generator.utils.FinancialCodeUtils; -import com.scottlogic.deg.generator.utils.IterableAsStream; import com.scottlogic.deg.generator.utils.JavaUtilRandomNumberGenerator; import org.junit.Assert; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.util.Iterator; import java.util.concurrent.atomic.AtomicInteger; +import static com.scottlogic.deg.generator.generation.string.generators.ChecksumStringGeneratorFactory.createIsinGenerator; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class IsinStringGeneratorTests { @Test public void shouldEndAllIsinsWithValidCheckDigit() { - IsinStringGenerator target = new IsinStringGenerator(); + StringGenerator target = createIsinGenerator(); final int NumberOfTests = 100; final Iterator allIsins = target.generateAllValues().iterator(); @@ -48,7 +51,7 @@ public void shouldEndAllIsinsWithValidCheckDigit() { @Test public void shouldEndAllRandomIsinsWithValidCheckDigit() { - IsinStringGenerator target = new IsinStringGenerator(); + StringGenerator target = createIsinGenerator(); final int NumberOfTests = 100; @@ -63,15 +66,11 @@ public void shouldEndAllRandomIsinsWithValidCheckDigit() { @Test public void shouldUseSedolWhenCountryIsGB() { - // this assumes that the first batch of values produced by the generator are GB-flavoured. If this changes in the future, this test might need to get more complicated - AtomicInteger numberOfIsinsTested = new AtomicInteger(0); - IterableAsStream.convert(new IsinStringGenerator().generateAllValues()) + createIsinGenerator().generateRandomValues(new JavaUtilRandomNumberGenerator()) + .filter(isinString -> isinString.substring(0, 2).equals("GB")) .limit(100) .forEach(isinString -> { - if (!isinString.substring(0, 2).equals("GB")) - throw new IllegalStateException("Test assumes that the first 100 ISINs will be GB-flavoured"); - assertThat( FinancialCodeUtils.isValidSedolNsin(isinString.substring(2, 11)), is(true)); @@ -83,8 +82,47 @@ public void shouldUseSedolWhenCountryIsGB() { } @Test + public void shouldUseCusipWhenCountryIsUS() { + AtomicInteger numberOfIsinsTested = new AtomicInteger(0); + createIsinGenerator().generateRandomValues(new JavaUtilRandomNumberGenerator()) + .filter(isinString -> isinString.substring(0, 2).equals("US")) + .limit(100) + .forEach(isinString -> { + assertThat( + FinancialCodeUtils.isValidCusipNsin(isinString.substring(2, 11)), is(true)); + + numberOfIsinsTested.incrementAndGet(); + }); + + // make sure we tested the number we expected + assertThat(numberOfIsinsTested.get(), equalTo(100)); + } + + @Test + public void shouldUseGeneralRegexWhenCountryIsNotGbOrUs() { + AtomicInteger numberOfIsinsTested = new AtomicInteger(0); + createIsinGenerator().generateRandomValues(new JavaUtilRandomNumberGenerator()) + .filter(isinString -> { + String countryCode = isinString.substring(0, 2); + return !countryCode.equals("GB") && !countryCode.equals("US"); + }) + .limit(100) + .forEach(isinString -> { + String APPROX_ISIN_REGEX = "[A-Z]{2}[A-Z0-9]{9}[0-9]"; + RegexStringGenerator regexStringGenerator = new RegexStringGenerator(APPROX_ISIN_REGEX, true); + assertTrue(regexStringGenerator.matches(isinString)); + + numberOfIsinsTested.incrementAndGet(); + }); + + // make sure we tested the number we expected + assertThat(numberOfIsinsTested.get(), equalTo(100)); + } + + @Test + @Disabled("Standard constraints e.g. ISINs currently cannot be negated") public void complementShouldProduceNoRandomValidIsins() { - StringGenerator target = new IsinStringGenerator().complement(); + StringGenerator target = createIsinGenerator().complement(); final int NumberOfTests = 100; @@ -98,36 +136,38 @@ public void complementShouldProduceNoRandomValidIsins() { @Test public void shouldMatchAValidIsinCodeWhenNotNegated(){ - StringGenerator isinGenerator = new IsinStringGenerator(); + StringGenerator isinGenerator = createIsinGenerator(); - boolean matches = isinGenerator.match("GB0002634946"); + boolean matches = isinGenerator.matches("GB0002634946"); Assert.assertTrue(matches); } @Test public void shouldNotMatchAnInvalidIsinCodeWhenNotNegated(){ - StringGenerator isinGenerator = new IsinStringGenerator(); + StringGenerator isinGenerator = createIsinGenerator(); - boolean matches = isinGenerator.match("not an isin"); + boolean matches = isinGenerator.matches("not an isin"); Assert.assertFalse(matches); } @Test + @Disabled("Standard constraints e.g. ISINs currently cannot be negated") public void shouldNotMatchAValidIsinCodeWhenNegated(){ - StringGenerator isinGenerator = new IsinStringGenerator().complement(); + StringGenerator isinGenerator = createIsinGenerator().complement(); - boolean matches = isinGenerator.match("GB0002634946"); + boolean matches = isinGenerator.matches("GB0002634946"); Assert.assertFalse(matches); } @Test + @Disabled("Standard constraints e.g. ISINs currently cannot be negated") public void shouldMatchAnInvalidIsinCodeWhenNegated(){ - StringGenerator isinGenerator = new IsinStringGenerator().complement(); + StringGenerator isinGenerator = createIsinGenerator().complement(); - boolean matches = isinGenerator.match("not an isin"); + boolean matches = isinGenerator.matches("not an isin"); Assert.assertTrue(matches); } diff --git a/generator/src/test/java/com/scottlogic/deg/generator/generation/string/RegexStringGeneratorTests.java b/generator/src/test/java/com/scottlogic/deg/generator/generation/string/RegexStringGeneratorTests.java index 2282b5538..8bd599072 100644 --- a/generator/src/test/java/com/scottlogic/deg/generator/generation/string/RegexStringGeneratorTests.java +++ b/generator/src/test/java/com/scottlogic/deg/generator/generation/string/RegexStringGeneratorTests.java @@ -16,7 +16,8 @@ package com.scottlogic.deg.generator.generation.string; -import com.scottlogic.deg.generator.utils.IterableAsStream; +import com.scottlogic.deg.generator.generation.string.generators.RegexStringGenerator; +import com.scottlogic.deg.generator.generation.string.generators.StringGenerator; import com.scottlogic.deg.generator.utils.JavaUtilRandomNumberGenerator; import org.hamcrest.core.Is; import org.junit.jupiter.api.BeforeEach; @@ -24,6 +25,7 @@ import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; import java.util.stream.StreamSupport; import static com.scottlogic.deg.generator.helpers.StringGeneratorHelper.assertGeneratorCanGenerateAtLeastOneStringWithinLengthBounds; @@ -105,10 +107,10 @@ void shouldCorrectlyReplaceCharacterGroups() { void generateInterestingValuesShouldGenerateShortestAndLongestValues() { StringGenerator generator = new RegexStringGenerator("Test_(\\d{3}|[A-Z]{5})_(banana|apple)", true); - Iterable resultsIterable = generator.generateInterestingValues(); + List results = generator.generateInterestingValues().collect(Collectors.toList()); assertThat( - resultsIterable, + results, containsInAnyOrder( "Test_000_apple", "Test_AAAAA_banana")); @@ -118,9 +120,9 @@ void generateInterestingValuesShouldGenerateShortestAndLongestValues() { void interestingValuesShouldBePrintable() { StringGenerator generator = new RegexStringGenerator("Test.Test", true); - Iterable resultsIterable = generator.generateInterestingValues(); + List results = generator.generateInterestingValues().collect(Collectors.toList()); - for (String interestingValue : resultsIterable) { + for (String interestingValue : results) { for (char character : interestingValue.toCharArray()) { assertThat(character, greaterThanOrEqualTo((char)32)); } @@ -131,43 +133,22 @@ void interestingValuesShouldBePrintable() { void interestingValuesShouldBeBounds() { StringGenerator generator = new RegexStringGenerator(".{10,20}", true); - Iterable resultsIterable = generator.generateInterestingValues(); - - ArrayList results = new ArrayList<>(); - - resultsIterable.iterator().forEachRemaining(results::add); + List results = generator.generateInterestingValues().collect(Collectors.toList()); assertThat(results.size(), Is.is(2)); assertThat(results.get(0).length(), Is.is(10)); assertThat(results.get(1).length(), Is.is(20)); } - @Test - void iterableShouldBeRepeatable() { - StringGenerator generator = new RegexStringGenerator("Test", true); - - Iterable resultsIterable = generator.generateInterestingValues(); - - resultsIterable.iterator().forEachRemaining(string -> { - }); // once - - resultsIterable.iterator().forEachRemaining(string -> { - }); // twice - } - @Test void shouldCreateZeroLengthInterestingValue() { StringGenerator generator = new RegexStringGenerator("(Test)?", true); - Iterable resultsIterable = generator.generateInterestingValues(); - - String[] sampleValues = - IterableAsStream.convert(resultsIterable) - .toArray(String[]::new); + List results = generator.generateInterestingValues().collect(Collectors.toList()); assertThat( - sampleValues, - arrayContainingInAnyOrder( + results, + containsInAnyOrder( "", "Test")); } @@ -176,12 +157,12 @@ void shouldCreateZeroLengthInterestingValue() { void shouldCorrectlySampleInfiniteResults() { StringGenerator generator = new RegexStringGenerator("[a]+", false); - Iterable resultsIterable = generator.generateRandomValues(new JavaUtilRandomNumberGenerator(0)); + Stream results = generator.generateRandomValues(new JavaUtilRandomNumberGenerator(0)); List sampleValues = - IterableAsStream.convert(resultsIterable) - .limit(1000) - .collect(Collectors.toList()); + results + .limit(1000) + .collect(Collectors.toList()); assertThat(sampleValues, not(contains(null, ""))); } @@ -238,8 +219,8 @@ void shouldReturnValuesWhenNonContradictingConstraints() { @Test void shouldNotGenerateInvalidUnicodeCodePoints() { StringGenerator generator = new RegexStringGenerator("[😁-😘]{1}", true); - Iterable resultsIterable = generator.generateAllValues(); - for (String s : resultsIterable) { + List results = generator.generateAllValues().collect(Collectors.toList()); + for (String s : results) { if (s != null && doesStringContainSurrogates(s)) { fail("string contains surrogate character"); } @@ -379,13 +360,13 @@ private void expectFirstResult(String expectedValue) { private void expectMatch(String subject, boolean matchFullString) { StringGenerator generator = constructGenerator(matchFullString); - assertTrue(generator.match(subject)); + assertTrue(generator.matches(subject)); } private void expectNoMatch(String subject, boolean matchFullString) { StringGenerator generator = constructGenerator(matchFullString); - assertFalse(generator.match(subject)); + assertFalse(generator.matches(subject)); } @Test @@ -414,11 +395,9 @@ void generateInterestingValues_withShorterThanAndContainingAndMatchingRegex_shou StringGenerator intersected = shorterThan.intersect(matchingRegex).intersect(containingRegex); - Iterable result = intersected.generateInterestingValues(); + List results = intersected.generateInterestingValues().collect(Collectors.toList()); - ArrayList strings = new ArrayList<>(); - result.iterator().forEachRemaining(strings::add); - assertThat(strings, not(empty())); + assertThat(results, not(empty())); } @Test @@ -428,18 +407,16 @@ void generateInterestingValues_withShorterThanAndMatchingRegex_shouldBeAbleToCre StringGenerator intersected = shorterThan.intersect(matchingRegex); - Iterable result = intersected.generateInterestingValues(); + List results = intersected.generateInterestingValues().collect(Collectors.toList()); - ArrayList strings = new ArrayList<>(); - result.iterator().forEachRemaining(strings::add); - assertThat(strings, not(empty())); + assertThat(results, not(empty())); } @Test void match_withInputMatchingRequiredLength_shouldMatch(){ RegexStringGenerator shorterThan = new RegexStringGenerator("^.{0,1}$", true); - boolean match = shorterThan.match("a"); + boolean match = shorterThan.matches("a"); assertThat(match, is(true)); } @@ -448,7 +425,7 @@ void match_withInputMatchingRequiredLength_shouldMatch(){ void match_withInputMatchingFirstRequiredLength_shouldMatch(){ RegexStringGenerator shorterThan = new RegexStringGenerator("^(.{0,1}|.{3,10})$", true); - boolean match = shorterThan.match("a"); + boolean match = shorterThan.matches("a"); assertThat(match, is(true)); } @@ -457,7 +434,7 @@ void match_withInputMatchingFirstRequiredLength_shouldMatch(){ void match_withInputMatchingSecondRequiredLength_shouldMatch(){ RegexStringGenerator shorterThan = new RegexStringGenerator("^(.{0,1}|.{3,10})$", true); - boolean match = shorterThan.match("aaa"); + boolean match = shorterThan.matches("aaa"); assertThat(match, is(true)); } @@ -466,7 +443,7 @@ void match_withInputMatchingSecondRequiredLength_shouldMatch(){ void match_withInputNotMatchingAnyRequiredLength_shouldNotMatch(){ RegexStringGenerator shorterThan = new RegexStringGenerator("^(.{0,1}|.{3,10})$", true); - boolean match = shorterThan.match("aa"); + boolean match = shorterThan.matches("aa"); assertThat(match, is(false)); } diff --git a/generator/src/test/java/com/scottlogic/deg/generator/generation/string/SedolStringGeneratorTests.java b/generator/src/test/java/com/scottlogic/deg/generator/generation/string/SedolStringGeneratorTests.java index 4199fdc97..f13eba1e0 100644 --- a/generator/src/test/java/com/scottlogic/deg/generator/generation/string/SedolStringGeneratorTests.java +++ b/generator/src/test/java/com/scottlogic/deg/generator/generation/string/SedolStringGeneratorTests.java @@ -16,44 +16,83 @@ package com.scottlogic.deg.generator.generation.string; +import com.scottlogic.deg.generator.generation.string.generators.StringGenerator; +import com.scottlogic.deg.generator.utils.FinancialCodeUtils; +import com.scottlogic.deg.generator.utils.JavaUtilRandomNumberGenerator; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import java.util.Iterator; + +import static com.scottlogic.deg.generator.generation.string.generators.ChecksumStringGeneratorFactory.createSedolGenerator; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class SedolStringGeneratorTests { + @Test + public void shouldEndAllSedolsWithValidCheckDigit() { + StringGenerator target = createSedolGenerator(); + final int NumberOfTests = 100; + + final Iterator allSedols = target.generateAllValues().iterator(); + + for (int i = 0; i < NumberOfTests; ++i) { + final String nextSedol = allSedols.next(); + final char checkDigit = FinancialCodeUtils.calculateSedolCheckDigit(nextSedol.substring(0, 6)); + assertThat(nextSedol.charAt(6), equalTo(checkDigit)); + } + } + + @Test + public void shouldEndAllRandomCusipsWithValidCheckDigit() { + StringGenerator target = createSedolGenerator(); + + final int NumberOfTests = 100; + + final Iterator allSedols = target.generateRandomValues(new JavaUtilRandomNumberGenerator()).iterator(); + + for (int i = 0; i < NumberOfTests; ++i) { + final String nextSedol = allSedols.next(); + final char checkDigit = FinancialCodeUtils.calculateSedolCheckDigit(nextSedol.substring(0, 6)); + assertThat(nextSedol.charAt(6), equalTo(checkDigit)); + } + } @Test public void shouldMatchAValidSedolCodeWhenNotNegated(){ - StringGenerator SedolGenerator = new SedolStringGenerator(); + StringGenerator SedolGenerator = createSedolGenerator(); - boolean matches = SedolGenerator.match("2634946"); + boolean matches = SedolGenerator.matches("2634946"); assertTrue(matches); } @Test public void shouldNotMatchAnInvalidSedolCodeWhenNotNegated(){ - StringGenerator SedolGenerator = new SedolStringGenerator(); + StringGenerator SedolGenerator = createSedolGenerator(); - boolean matches = SedolGenerator.match("not a sedol"); + boolean matches = SedolGenerator.matches("not a sedol"); assertFalse(matches); } @Test + @Disabled("Standard constraints e.g. ISINs currently cannot be negated") public void shouldNotMatchAValidSedolCodeWhenNegated(){ - StringGenerator SedolGenerator = new SedolStringGenerator().complement(); + StringGenerator SedolGenerator = createSedolGenerator().complement(); - boolean matches = SedolGenerator.match("2634946"); + boolean matches = SedolGenerator.matches("2634946"); assertFalse(matches); } @Test + @Disabled("Standard constraints e.g. ISINs currently cannot be negated") public void shouldMatchAnInvalidSedolCodeWhenNegated(){ - StringGenerator SedolGenerator = new SedolStringGenerator().complement(); + StringGenerator SedolGenerator = createSedolGenerator().complement(); - boolean matches = SedolGenerator.match("not a sedol"); + boolean matches = SedolGenerator.matches("not a sedol"); assertTrue(matches); } diff --git a/generator/src/test/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumStringGeneratorTests.java b/generator/src/test/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumStringGeneratorTests.java new file mode 100644 index 000000000..68918fea3 --- /dev/null +++ b/generator/src/test/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumStringGeneratorTests.java @@ -0,0 +1,63 @@ +package com.scottlogic.deg.generator.generation.string.generators; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/* + * Copyright 2019 Scott Logic Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class ChecksumStringGeneratorTests { + @Test + void matches_withInvalidPreChecksum_returnsFalse() { + String preChecksum = "abc"; + Character checksum = 'a'; + StringGenerator mockGenerator = mock(StringGenerator.class); + when(mockGenerator.matches(preChecksum)).thenReturn(false); + ChecksumMaker mockChecksumMaker = input -> input.charAt(0); + + ChecksumStringGenerator checksumStringGenerator = new ChecksumStringGenerator(mockGenerator, mockChecksumMaker); + + assertFalse(checksumStringGenerator.matches(preChecksum + checksum)); + } + + @Test + void matches_withInvalidChecksum_returnsFalse() { + String preChecksum = "abc"; + Character checksum = 'b'; + StringGenerator mockGenerator = mock(StringGenerator.class); + when(mockGenerator.matches(preChecksum)).thenReturn(true); + ChecksumMaker mockChecksumMaker = input -> input.charAt(0); + + ChecksumStringGenerator checksumStringGenerator = new ChecksumStringGenerator(mockGenerator, mockChecksumMaker); + + assertFalse(checksumStringGenerator.matches(preChecksum + checksum)); + } + + @Test + void matches_withValidString_returnsTrue() { + String preChecksum = "abc"; + Character checksum = 'a'; + StringGenerator mockGenerator = mock(StringGenerator.class); + when(mockGenerator.matches(preChecksum)).thenReturn(true); + ChecksumMaker mockChecksumMaker = input -> input.charAt(0); + + ChecksumStringGenerator checksumStringGenerator = new ChecksumStringGenerator(mockGenerator, mockChecksumMaker); + + assertTrue(checksumStringGenerator.matches(preChecksum + checksum)); + } +} diff --git a/generator/src/test/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumlessIsinGeneratorTests.java b/generator/src/test/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumlessIsinGeneratorTests.java new file mode 100644 index 000000000..2aad5eda8 --- /dev/null +++ b/generator/src/test/java/com/scottlogic/deg/generator/generation/string/generators/ChecksumlessIsinGeneratorTests.java @@ -0,0 +1,55 @@ +package com.scottlogic.deg.generator.generation.string.generators; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* + * Copyright 2019 Scott Logic Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class ChecksumlessIsinGeneratorTests { + @Test + void matches_withInvalidCountryCode_returnsFalse() { + String checksumlessIsin = "A000000KPAL"; + ChecksumlessIsinGenerator generator = new ChecksumlessIsinGenerator(); + + assertFalse(generator.matches(checksumlessIsin)); + } + + @Test + void matches_withInvalidLength_returnsFalse() { + String checksumlessIsin = "ADKPAL"; + ChecksumlessIsinGenerator generator = new ChecksumlessIsinGenerator(); + + assertFalse(generator.matches(checksumlessIsin)); + } + + @Test + void matches_withInvalidGbCode_returnsFalse() { + String checksumlessIsin = "GB11RJ6BYL2"; + ChecksumlessIsinGenerator generator = new ChecksumlessIsinGenerator(); + + assertFalse(generator.matches(checksumlessIsin)); + } + + @Test + void matches_withValidString_returnsTrue() { + String checksumlessIsin = "AD00000KPAL"; + ChecksumlessIsinGenerator generator = new ChecksumlessIsinGenerator(); + + assertTrue(generator.matches(checksumlessIsin)); + } +} diff --git a/generator/src/test/java/com/scottlogic/deg/generator/generation/string/generators/PrefixingStringGeneratorTests.java b/generator/src/test/java/com/scottlogic/deg/generator/generation/string/generators/PrefixingStringGeneratorTests.java new file mode 100644 index 000000000..d606330c0 --- /dev/null +++ b/generator/src/test/java/com/scottlogic/deg/generator/generation/string/generators/PrefixingStringGeneratorTests.java @@ -0,0 +1,55 @@ +/* + * Copyright 2019 Scott Logic Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.scottlogic.deg.generator.generation.string.generators; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class PrefixingStringGeneratorTests { + @Test + void matches_withInvalidPrefix_returnsFalse() { + String testPrefix = "BB"; + String testSuffix = "penguins"; + StringGenerator mockGenerator = mock(StringGenerator.class); + when(mockGenerator.matches(testSuffix)).thenReturn(true); + PrefixingStringGenerator prefixingStringGenerator = new PrefixingStringGenerator("AA", mockGenerator); + assertFalse(prefixingStringGenerator.matches(testPrefix + testSuffix)); + } + + @Test + void matches_withInvalidSuffix_returnsFalse() { + String testPrefix = "AA"; + String testSuffix = "penguins"; + StringGenerator mockGenerator = mock(StringGenerator.class); + when(mockGenerator.matches(testSuffix)).thenReturn(false); + PrefixingStringGenerator prefixingStringGenerator = new PrefixingStringGenerator("AA", mockGenerator); + assertFalse(prefixingStringGenerator.matches(testPrefix + testSuffix)); + } + + @Test + void matches_withValidString_returnsTrue() { + String testPrefix = "AA"; + String testSuffix = "penguins"; + StringGenerator mockGenerator = mock(StringGenerator.class); + when(mockGenerator.matches(testSuffix)).thenReturn(true); + PrefixingStringGenerator prefixingStringGenerator = new PrefixingStringGenerator("AA", mockGenerator); + assertTrue(prefixingStringGenerator.matches(testPrefix + testSuffix)); + } +} diff --git a/generator/src/test/java/com/scottlogic/deg/generator/helpers/StringGeneratorHelper.java b/generator/src/test/java/com/scottlogic/deg/generator/helpers/StringGeneratorHelper.java index a1c8b4834..96e91e19f 100644 --- a/generator/src/test/java/com/scottlogic/deg/generator/helpers/StringGeneratorHelper.java +++ b/generator/src/test/java/com/scottlogic/deg/generator/helpers/StringGeneratorHelper.java @@ -16,7 +16,7 @@ package com.scottlogic.deg.generator.helpers; -import com.scottlogic.deg.generator.generation.string.StringGenerator; +import com.scottlogic.deg.generator.generation.string.generators.StringGenerator; import org.junit.Assert; import java.util.Iterator; diff --git a/generator/src/test/java/com/scottlogic/deg/generator/restrictions/TextualRestrictionsTests.java b/generator/src/test/java/com/scottlogic/deg/generator/restrictions/TextualRestrictionsTests.java index fde91e293..cbbb30eda 100644 --- a/generator/src/test/java/com/scottlogic/deg/generator/restrictions/TextualRestrictionsTests.java +++ b/generator/src/test/java/com/scottlogic/deg/generator/restrictions/TextualRestrictionsTests.java @@ -17,10 +17,10 @@ package com.scottlogic.deg.generator.restrictions; import com.scottlogic.deg.common.profile.constraints.atomic.StandardConstraintTypes; -import com.scottlogic.deg.generator.generation.string.IsinStringGenerator; -import com.scottlogic.deg.generator.generation.string.RegexStringGenerator; -import com.scottlogic.deg.generator.generation.string.StringGenerator; +import com.scottlogic.deg.generator.generation.string.generators.RegexStringGenerator; +import com.scottlogic.deg.generator.generation.string.generators.StringGenerator; import org.junit.Assert; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.util.Collections; @@ -30,6 +30,7 @@ import static com.scottlogic.deg.generator.helpers.StringGeneratorHelper.*; import static org.hamcrest.Matchers.*; import static org.hamcrest.core.IsNull.nullValue; +import static org.junit.jupiter.api.Assertions.assertTrue; class TextualRestrictionsTests { @Test @@ -392,26 +393,26 @@ void createGenerator_withMinAndMaxLengthAndContainingRegexConstraint_shouldCreat @Test void createGenerator_withOnlyAMatchingStandardConstraint_shouldCreateSomeStrings() { - StringRestrictions restrictions = aValid(StandardConstraintTypes.ISIN, false); + StringRestrictions restrictions = aValid(StandardConstraintTypes.ISIN); StringGenerator generator = restrictions.createGenerator(); - Assert.assertThat(generator, instanceOf(IsinStringGenerator.class)); + assertTrue(generator.generateAllValues().limit(1).count() > 0); } @Test void createGenerator_withMinLengthAndMatchingStandardConstraint_shouldCreateSomeStrings() { - StringRestrictions restrictions = aValid(StandardConstraintTypes.ISIN, false) + StringRestrictions restrictions = aValid(StandardConstraintTypes.ISIN) .intersect(minLength(1)).restrictions; StringGenerator generator = restrictions.createGenerator(); - Assert.assertThat(generator, instanceOf(IsinStringGenerator.class)); + assertTrue(generator.generateAllValues().limit(1).count() > 0); } @Test void createGenerator_withMaxLengthShorterThanCodeLengthAndMatchingStandardConstraint_shouldCreateNoStrings() { - MergeResult intersect = aValid(StandardConstraintTypes.ISIN, false) + MergeResult intersect = aValid(StandardConstraintTypes.ISIN) .intersect(maxLength(10)); Assert.assertFalse(intersect.successful); @@ -419,37 +420,38 @@ void createGenerator_withMaxLengthShorterThanCodeLengthAndMatchingStandardConstr @Test void createGenerator_withMaxLengthAtLengthOfCodeLengthAndMatchingStandardConstraint_shouldCreateSomeStrings() { - StringRestrictions restrictions = aValid(StandardConstraintTypes.ISIN, false) + StringRestrictions restrictions = aValid(StandardConstraintTypes.ISIN) .intersect(maxLength(12)).restrictions; StringGenerator generator = restrictions.createGenerator(); - Assert.assertThat(generator, instanceOf(IsinStringGenerator.class)); + assertTrue(generator.generateAllValues().limit(1).count() > 0); } @Test void createGenerator_withMaxLengthLongerThanCodeLengthAndMatchingStandardConstraint_shouldCreateSomeStrings() { - StringRestrictions restrictions = aValid(StandardConstraintTypes.ISIN, false) + StringRestrictions restrictions = aValid(StandardConstraintTypes.ISIN) .intersect(maxLength(100)).restrictions; StringGenerator generator = restrictions.createGenerator(); - Assert.assertThat(generator, instanceOf(IsinStringGenerator.class)); + assertTrue(generator.generateAllValues().limit(1).count() > 0); } @Test void createGenerator_withOfLengthAndMatchingStandardConstraint_shouldCreateSomeStrings() { - StringRestrictions restrictions = aValid(StandardConstraintTypes.ISIN, false) + StringRestrictions restrictions = aValid(StandardConstraintTypes.ISIN) .intersect(ofLength(12, false)).restrictions; StringGenerator generator = restrictions.createGenerator(); - Assert.assertThat(generator, instanceOf(IsinStringGenerator.class)); + assertTrue(generator.generateAllValues().limit(1).count() > 0); } @Test + @Disabled("Regex constraints cannot currently be combined with standard constraints e.g. ISINs") void createGenerator_withMatchingRegexAndMatchingStandardConstraint_shouldCreateStrings() { - StringRestrictions restrictions = aValid(StandardConstraintTypes.ISIN, false) + StringRestrictions restrictions = aValid(StandardConstraintTypes.ISIN) .intersect(matchingRegex("[a-zA-Z0-9]{12}", false)).restrictions; StringGenerator generator = restrictions.createGenerator(); @@ -458,8 +460,9 @@ void createGenerator_withMatchingRegexAndMatchingStandardConstraint_shouldCreate } @Test + @Disabled("Regex constraints cannot currently be combined with standard constraints e.g. ISINs") void createGenerator_withContainingRegexAndMatchingStandardConstraint_shouldCreateStrings() { - StringRestrictions restrictions = aValid(StandardConstraintTypes.ISIN, false) + StringRestrictions restrictions = aValid(StandardConstraintTypes.ISIN) .intersect(containsRegex("[a-zA-Z0-9]{12}", false)).restrictions; StringGenerator generator = restrictions.createGenerator(); @@ -686,8 +689,8 @@ private static StringRestrictions containsRegex(String regex, @SuppressWarnings( negate ? Collections.singleton(pattern) : Collections.emptySet()); } - private static StringRestrictions aValid(@SuppressWarnings("SameParameterValue") StandardConstraintTypes type, @SuppressWarnings("SameParameterValue") boolean negate){ - return new MatchesStandardStringRestrictions(type, negate); + private static StringRestrictions aValid(StandardConstraintTypes type){ + return new MatchesStandardStringRestrictions(type); } private static void assertGeneratorCannotGenerateAnyStrings(StringGenerator generator) { diff --git a/orchestrator/src/test/java/com/scottlogic/deg/orchestrator/cucumber/features/Random.feature b/orchestrator/src/test/java/com/scottlogic/deg/orchestrator/cucumber/features/Random.feature index ca8362631..514196b7c 100644 --- a/orchestrator/src/test/java/com/scottlogic/deg/orchestrator/cucumber/features/Random.feature +++ b/orchestrator/src/test/java/com/scottlogic/deg/orchestrator/cucumber/features/Random.feature @@ -66,6 +66,7 @@ Feature: User can generate valid data for all types (string, integer, decimal, o Then 5 rows of data are generated And foo contains strings matching /[A-Z]{2}[A-Z0-9]{9}[0-9]{1}/ + @ignore "Standard constraints e.g. ISINs cannot yet be combined with not equal to constraints." Scenario: The generator produces valid ISIN data in random mode when combined with a not equal to constraint Given foo is of type "ISIN" And foo is anything but equal to "GB009CJ9GB62" @@ -80,6 +81,7 @@ Feature: User can generate valid data for all types (string, integer, decimal, o Then 5 rows of data are generated And foo contains strings matching /[B-DF-HJ-NP-TV-Z0-9]{6}[0-9]/ + @ignore "Standard constraints e.g. ISINs cannot yet be combined with not equal to constraints." Scenario: The generator produces valid SEDOL data in random mode when combined with a not equal to constraint Given foo is of type "SEDOL" And foo is anything but equal to "3091357" @@ -94,6 +96,7 @@ Feature: User can generate valid data for all types (string, integer, decimal, o Then 5 rows of data are generated And foo contains strings matching /[0-9]{3}[A-Z0-9]{5}[0-9]/ + @ignore "Standard constraints e.g. ISINs cannot yet be combined with not equal to constraints." Scenario: The generator produces valid CUSIP data in random mode when combined with a not equal to constraint Given foo is of type "CUSIP" And foo is anything but equal to "594918104" diff --git a/orchestrator/src/test/java/com/scottlogic/deg/orchestrator/cucumber/features/operators/general/OfTypeFinancialCodes.feature b/orchestrator/src/test/java/com/scottlogic/deg/orchestrator/cucumber/features/operators/general/OfTypeFinancialCodes.feature index b8a38c290..6f5a3f35a 100644 --- a/orchestrator/src/test/java/com/scottlogic/deg/orchestrator/cucumber/features/operators/general/OfTypeFinancialCodes.feature +++ b/orchestrator/src/test/java/com/scottlogic/deg/orchestrator/cucumber/features/operators/general/OfTypeFinancialCodes.feature @@ -19,10 +19,10 @@ Feature: User can specify that a field must be a financial code type And the generator can generate at most 4 rows Then the following data should be generated: | foo | - | "GB0000000009" | - | "GB0000000116" | - | "GB0000000223" | - | "GB0000000330" | + | "AD0000000003" | + | "AD0000000011" | + | "AD0000000029" | + | "AD0000000037" | Scenario: An ofType constraint with the value "isin" fails with an invalid profile error message Given foo is of type "isin" @@ -82,6 +82,7 @@ Feature: User can specify that a field must be a financial code type | null | | "GB0002634946" | + @ignore "Standard constraints e.g. ISINs cannot yet be negated." Scenario: An ISIN constraint combined with a non-ISIN constraint generates no data Given foo is of type "ISIN" And foo is anything but null @@ -322,6 +323,7 @@ Feature: User can specify that a field must be a financial code type | "GB0002634946" | | null | + @ignore "Standard constraints e.g. ISINs cannot yet be combined with regex constraints." Scenario: An ISIN constraint combined with a matching regex constraint that matches valid ISINs should generate matching valid ISINs Given foo is of type "ISIN" And foo is matching regex "US9311421039" @@ -330,6 +332,7 @@ Feature: User can specify that a field must be a financial code type | "US9311421039" | | null | + @ignore "Standard constraints e.g. ISINs cannot yet be combined with regex constraints." Scenario: An ISIN constraint combined with a matching regex constraint that cannot match any valid ISIN due to its length should only generate null Given foo is of type "ISIN" And foo is matching regex "US[0-9]{9}" @@ -337,6 +340,7 @@ Feature: User can specify that a field must be a financial code type | foo | | null | + @ignore "Standard constraints e.g. ISINs cannot yet be combined with regex constraints." Scenario: An ISIN constraint combined with a matching regex constraint that cannot match any valid ISIN due to its check digit should only generate null Given foo is of type "ISIN" And foo is matching regex "US9311421038" @@ -354,6 +358,7 @@ Feature: User can specify that a field must be a financial code type | null | | "0263494" | + @ignore "Standard constraints e.g. ISINs cannot yet be negated." Scenario: A SEDOL constraint combined with a non-SEDOL constraint generates no data Given foo is of type "SEDOL" And foo is anything but null @@ -581,6 +586,7 @@ Feature: User can specify that a field must be a financial code type | "0263494" | | null | + @ignore "Standard constraints e.g. ISINs cannot yet be combined with date constraints." Scenario: A SEDOL constraint combined with a not before or at constraint generates valid SEDOLs Given foo is of type "SEDOL" And foo is anything but before or at 2019-01-01T00:00:00.000Z @@ -594,6 +600,7 @@ Feature: User can specify that a field must be a financial code type | "0263494" | | null | + @ignore "Standard constraints e.g. ISINs cannot yet be combined with regex constraints." Scenario: A SEDOL constraint combined with a matching regex constraint that matches valid SEDOLs should generate valid SEDOLs Given foo is of type "SEDOL" And foo is matching regex "0263494" @@ -602,6 +609,7 @@ Feature: User can specify that a field must be a financial code type | "0263494" | | null | + @ignore "Standard constraints e.g. ISINs cannot yet be combined with regex constraints." Scenario: A SEDOL constraint combined with a matching regex constraint that cannot match any valid SEDOL because it has the wrong check digit should only generate null Given foo is of type "SEDOL" And foo is matching regex "0263492" @@ -609,6 +617,7 @@ Feature: User can specify that a field must be a financial code type | foo | | null | + @ignore "Standard constraints e.g. ISINs cannot yet be combined with regex constraints." Scenario: A SEDOL constraint combined with a matching regex constraint that cannot match any valid SEDOL because it has the wrong length should only generate null Given foo is of type "SEDOL" And foo is matching regex "[0-9]{6}" @@ -616,6 +625,7 @@ Feature: User can specify that a field must be a financial code type | foo | | null | + @ignore "Standard constraints e.g. ISINs cannot yet be combined with regex constraints." Scenario: A SEDOL constraint combined with a matching regex constraint that cannot match any valid SEDOL because it cannot have a correct check digit should only generate null Given foo is of type "SEDOL" And foo is matching regex "0[023]63492" @@ -633,6 +643,7 @@ Feature: User can specify that a field must be a financial code type | null | | "38259P508" | + @ignore "Standard constraints e.g. ISINs cannot yet be negated." Scenario: A CUSIP constraint combined with a non-CUSIP constraint generates no data Given foo is of type "CUSIP" And foo is anything but null @@ -873,6 +884,7 @@ Feature: User can specify that a field must be a financial code type | "38259P508" | | null | + @ignore "Standard constraints e.g. ISINs cannot yet be combined with regex constraints." Scenario: A CUSIP constraint combined with a matching regex constraint that matches a valid CUSIP generates valid CUSIPs Given foo is of type "CUSIP" And foo is matching regex "38259P508" @@ -881,6 +893,7 @@ Feature: User can specify that a field must be a financial code type | "38259P508" | | null | + @ignore "Standard constraints e.g. ISINs cannot yet be combined with regex constraints." Scenario: A CUSIP constraint combined with a matching regex constraint that cannot match a valid CUSIP because it has an invalid check digit should only generate null Given foo is of type "CUSIP" And foo is matching regex "38259P509" @@ -888,6 +901,7 @@ Feature: User can specify that a field must be a financial code type | foo | | null | + @ignore "Standard constraints e.g. ISINs cannot yet be combined with regex constraints." Scenario: A CUSIP constraint combined with a matching regex constraint that cannot match a valid CUSIP because it has the wrong length should only generate null Given foo is of type "CUSIP" And foo is matching regex "[0-9]{3}.{4}[0-9]" diff --git a/orchestrator/src/test/java/com/scottlogic/deg/orchestrator/cucumber/features/operators/string/LongerThan.feature b/orchestrator/src/test/java/com/scottlogic/deg/orchestrator/cucumber/features/operators/string/LongerThan.feature index 6c4384e1c..9fa67d8a5 100644 --- a/orchestrator/src/test/java/com/scottlogic/deg/orchestrator/cucumber/features/operators/string/LongerThan.feature +++ b/orchestrator/src/test/java/com/scottlogic/deg/orchestrator/cucumber/features/operators/string/LongerThan.feature @@ -229,6 +229,7 @@ Feature: User can specify that a string length is longer than, a specified numbe | "GB00YG2XYC52" | | "US0378331005" | + @ignore "Standard constraints e.g. ISINs cannot yet be negated." Scenario: A longer than constraint combined with a non-ISIN constraint generates data that matches the longer than constraint and contains no valid ISINs Given foo is longer than 2 And foo is anything but null @@ -263,6 +264,7 @@ Feature: User can specify that a string length is longer than, a specified numbe | foo | | null | + @ignore "Standard constraints e.g. ISINs cannot yet be negated." Scenario: A not longer than constraint combined with a non-ISIN constraint generates data that contains no valid ISINs Given foo is anything but longer than 12 And foo is anything but null @@ -290,6 +292,7 @@ Feature: User can specify that a string length is longer than, a specified numbe | "0263494" | | "3091357" | + @ignore "Standard constraints e.g. ISINs cannot yet be negated." Scenario: A longer than constraint combined with a non-SEDOL constraint generates data that matches the longer than constraint and contains no valid SEDOLs Given foo is longer than 2 And foo is anything but null @@ -324,6 +327,7 @@ Feature: User can specify that a string length is longer than, a specified numbe | foo | | null | + @ignore "Standard constraints e.g. ISINs cannot yet be negated." Scenario: A not longer than constraint combined with a non-SEDOL constraint generates data that matches the longer than constraint and contains no valid SEDOLs Given foo is anything but longer than 7 And foo is anything but null @@ -351,6 +355,7 @@ Feature: User can specify that a string length is longer than, a specified numbe | "38259P508" | | "594918104" | + @ignore "Standard constraints e.g. ISINs cannot yet be negated." Scenario: A longer than constraint combined with a non-CUSIP constraint generates data that matches the longer than constraint and contains no valid CUSIPs Given foo is longer than 2 And foo is anything but null @@ -385,6 +390,7 @@ Feature: User can specify that a string length is longer than, a specified numbe | foo | | null | + @ignore "Standard constraints e.g. ISINs cannot yet be negated." Scenario: A not longer than constraint combined with a non-CUSIP constraint generates data that matches the not longer than constraint and contains no valid CUSIPs Given foo is anything but longer than 9 And foo is anything but null