From caba76e58527da827107bd13a8b73b01b150b354 Mon Sep 17 00:00:00 2001 From: Ryan Stuart Date: Mon, 2 Dec 2019 13:21:49 +0000 Subject: [PATCH 1/2] fix(#1489): Fix equal to offset for whitelist Partial fix for the ticket #1489. This fixes the equal to case, but doesn't address the before or after cases. The reason for a partial fix is due to the difficulty comparing the two different cases (equal vs before/after). Equal to is simple because all of the values can be adjusted by the offset, translating a set of values to a set of values. Before/after are more complicated, as theoretically each of the values must be translated to a range. This therefore means the result is a set of restrictions. Unfortunately, the system isn't designed to be passing around a set of restrictions at this time in the program. Therefore, this will take further thought. A possible solution is to take the most restrictive of the values. This would ensure that whichever values are picked are permissable, but would severely limit the number of values output. --- .../relations/EqualToOffsetRelation.java | 10 +++- ...lationTest.java => AfterRelationTest.java} | 2 +- ...ationTest.java => BeforeRelationTest.java} | 2 +- ...tionTest.java => EqualToRelationTest.java} | 2 +- .../relations/FieldSpecRelationTest.java | 57 ++++++++++++++----- examples/integerOffsetFromSet/README.md | 3 + examples/integerOffsetFromSet/profile.json | 24 ++++++++ 7 files changed, 81 insertions(+), 19 deletions(-) rename core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/{AfterDateRelationTest.java => AfterRelationTest.java} (99%) rename core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/{BeforeDateRelationTest.java => BeforeRelationTest.java} (99%) rename core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/{EqualToDateRelationTest.java => EqualToRelationTest.java} (98%) create mode 100644 examples/integerOffsetFromSet/README.md create mode 100644 examples/integerOffsetFromSet/profile.json diff --git a/core/src/main/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/EqualToOffsetRelation.java b/core/src/main/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/EqualToOffsetRelation.java index 87fcffd19..1cc3c8ced 100644 --- a/core/src/main/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/EqualToOffsetRelation.java +++ b/core/src/main/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/EqualToOffsetRelation.java @@ -19,12 +19,16 @@ import com.scottlogic.datahelix.generator.common.profile.Field; import com.scottlogic.datahelix.generator.common.profile.FieldType; import com.scottlogic.datahelix.generator.common.profile.Granularity; +import com.scottlogic.datahelix.generator.common.whitelist.WeightedElement; import com.scottlogic.datahelix.generator.core.fieldspecs.*; import com.scottlogic.datahelix.generator.common.whitelist.DistributedList; import com.scottlogic.datahelix.generator.core.generation.databags.DataBagValue; import com.scottlogic.datahelix.generator.core.profile.constraints.Constraint; import com.scottlogic.datahelix.generator.core.restrictions.linear.LinearRestrictions; +import java.util.List; +import java.util.stream.Collectors; + public class EqualToOffsetRelation> implements FieldSpecRelation { private final Field main; @@ -48,7 +52,11 @@ public FieldSpec createModifierFromOtherFieldSpec(FieldSpec otherFieldSpec) { return FieldSpecFactory.nullOnly(); } if (otherFieldSpec instanceof WhitelistFieldSpec) { - throw new UnsupportedOperationException("cannot combine sets with equal to offset relation, Issue #1489"); + WhitelistFieldSpec whitelistFieldSpec = (WhitelistFieldSpec) otherFieldSpec; + List> modified = whitelistFieldSpec.getWhitelist().distributedList().stream() + .map(x -> new WeightedElement<>(offsetGranularity.getNext((T) x.element(), offset), x.weight())) + .collect(Collectors.toList()); + return FieldSpecFactory.fromList((DistributedList) new DistributedList<>(modified)); } LinearRestrictions otherRestrictions = (LinearRestrictions)((RestrictionsFieldSpec) otherFieldSpec).getRestrictions(); diff --git a/core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/AfterDateRelationTest.java b/core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/AfterRelationTest.java similarity index 99% rename from core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/AfterDateRelationTest.java rename to core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/AfterRelationTest.java index 5e6326749..5881278fe 100644 --- a/core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/AfterDateRelationTest.java +++ b/core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/AfterRelationTest.java @@ -31,7 +31,7 @@ import static com.scottlogic.datahelix.generator.common.util.Defaults.ISO_MAX_DATE; import static org.junit.jupiter.api.Assertions.assertEquals; -public class AfterDateRelationTest { +public class AfterRelationTest { private final Field a = new Field("a", StandardSpecificFieldType.DATETIME.toSpecificFieldType(), false, "", false, false, null); private final Field b = new Field("b", StandardSpecificFieldType.DATETIME.toSpecificFieldType(), false, "", false, false, null); diff --git a/core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/BeforeDateRelationTest.java b/core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/BeforeRelationTest.java similarity index 99% rename from core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/BeforeDateRelationTest.java rename to core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/BeforeRelationTest.java index d417140ef..f0c2f59a4 100644 --- a/core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/BeforeDateRelationTest.java +++ b/core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/BeforeRelationTest.java @@ -31,7 +31,7 @@ import static com.scottlogic.datahelix.generator.common.util.Defaults.ISO_MIN_DATE; import static org.junit.jupiter.api.Assertions.assertEquals; -public class BeforeDateRelationTest { +public class BeforeRelationTest { private final Field a = new Field("a", StandardSpecificFieldType.DATETIME.toSpecificFieldType(), false, "", false, false, null); private final Field b = new Field("b", StandardSpecificFieldType.DATETIME.toSpecificFieldType(), false, "", false, false, null); diff --git a/core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/EqualToDateRelationTest.java b/core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/EqualToRelationTest.java similarity index 98% rename from core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/EqualToDateRelationTest.java rename to core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/EqualToRelationTest.java index 92c5be8ce..4df07cb61 100644 --- a/core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/EqualToDateRelationTest.java +++ b/core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/EqualToRelationTest.java @@ -30,7 +30,7 @@ import static com.shazam.shazamcrest.MatcherAssert.assertThat; import static com.shazam.shazamcrest.matcher.Matchers.sameBeanAs; -class EqualToDateRelationTest { +class EqualToRelationTest { private final Field a = new Field("a", StandardSpecificFieldType.DATETIME.toSpecificFieldType(), false ,"", false, false, null); private final Field b = new Field("b", StandardSpecificFieldType.DATETIME.toSpecificFieldType(), false, "", false, false, null); diff --git a/core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/FieldSpecRelationTest.java b/core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/FieldSpecRelationTest.java index 6bf1320b0..e15496455 100644 --- a/core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/FieldSpecRelationTest.java +++ b/core/src/test/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/FieldSpecRelationTest.java @@ -15,19 +15,23 @@ */ package com.scottlogic.datahelix.generator.core.fieldspecs.relations; -import com.scottlogic.datahelix.generator.common.profile.DateTimeGranularity; -import com.scottlogic.datahelix.generator.common.profile.Field; -import com.scottlogic.datahelix.generator.common.profile.SpecificFieldType; -import com.scottlogic.datahelix.generator.common.profile.StandardSpecificFieldType; +import com.scottlogic.datahelix.generator.common.profile.*; import com.scottlogic.datahelix.generator.common.util.defaults.DateTimeDefaults; +import com.scottlogic.datahelix.generator.common.whitelist.DistributedList; import com.scottlogic.datahelix.generator.core.fieldspecs.FieldSpec; import com.scottlogic.datahelix.generator.core.fieldspecs.FieldSpecFactory; import com.scottlogic.datahelix.generator.core.fieldspecs.RestrictionsFieldSpec; +import com.scottlogic.datahelix.generator.core.fieldspecs.WhitelistFieldSpec; import com.scottlogic.datahelix.generator.core.restrictions.linear.LinearRestrictions; +import com.scottlogic.datahelix.generator.core.restrictions.linear.LinearRestrictionsFactory; import org.junit.jupiter.api.Test; +import java.math.BigDecimal; import java.time.OffsetDateTime; import java.time.ZoneOffset; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static com.scottlogic.datahelix.generator.common.profile.FieldBuilder.createField; import static com.scottlogic.datahelix.generator.common.util.Defaults.ISO_MAX_DATE; @@ -38,13 +42,12 @@ import static java.time.temporal.ChronoUnit.YEARS; import static org.junit.Assert.assertEquals; -class FieldSpecRelationTest -{ +class FieldSpecRelationTest { private Field main = createField("main", StandardSpecificFieldType.DATETIME.toSpecificFieldType()); private Field other = createField("other", StandardSpecificFieldType.DATETIME.toSpecificFieldType()); @Test - public void equalTo_exactValue_returnsSame(){ + public void equalTo_exactValue_returnsSame() { FieldSpec fieldSpec = forYears(2018, 2018); EqualToRelation relation = new EqualToRelation(main, other); @@ -55,7 +58,7 @@ public void equalTo_exactValue_returnsSame(){ } @Test - public void equalTo_range_returnsSame(){ + public void equalTo_range_returnsSame() { FieldSpec fieldSpec = forYears(2018, 2020); EqualToRelation relation = new EqualToRelation(main, other); @@ -66,7 +69,7 @@ public void equalTo_range_returnsSame(){ } @Test - public void afterOrAt_exactValue_returnsBetween(){ + public void afterOrAt_exactValue_returnsBetween() { FieldSpec fieldSpec = forYears(2018, 2018); DateTimeGranularity offsetGranularity = DateTimeGranularity.create("MILLIS"); AfterRelation relation = new AfterRelation(main, other, true, DateTimeDefaults.get(), offsetGranularity, 0); @@ -78,7 +81,7 @@ public void afterOrAt_exactValue_returnsBetween(){ } @Test - public void afterOrAt_range_returnsFromMin(){ + public void afterOrAt_range_returnsFromMin() { FieldSpec fieldSpec = forYears(2018, 2020); DateTimeGranularity offsetGranularity = DateTimeGranularity.create("MILLIS"); AfterRelation relation = new AfterRelation(main, other, true, DateTimeDefaults.get(), offsetGranularity, 0); @@ -104,14 +107,14 @@ public void after_range_returnsFromMin() { OffsetDateTime expectedMin = OffsetDateTime.of(minYear, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC) .plusNanos(1000000); // Current smallest granularity is milliseconds. - OffsetDateTime expectedMax =DateTimeDefaults.get().max(); + OffsetDateTime expectedMax = DateTimeDefaults.get().max(); assertEquals(actualMin.compareTo(expectedMin), 0); assertEquals(actualMax.compareTo(expectedMax), 0); } @Test - public void beforeOrAt_exactValue_returnsBetween(){ + public void beforeOrAt_exactValue_returnsBetween() { FieldSpec fieldSpec = forYears(2018, 2018); DateTimeGranularity offsetGranularity = DateTimeGranularity.create("MILLIS"); BeforeRelation relation = new BeforeRelation(main, other, true, DateTimeDefaults.get(), offsetGranularity, 0); @@ -123,7 +126,7 @@ public void beforeOrAt_exactValue_returnsBetween(){ } @Test - public void beforeOrAt_range_returnsFromMin(){ + public void beforeOrAt_range_returnsFromMin() { FieldSpec fieldSpec = forYears(2018, 2020); DateTimeGranularity offsetGranularity = DateTimeGranularity.create("MILLIS"); BeforeRelation relation = new BeforeRelation(main, other, true, DateTimeDefaults.get(), offsetGranularity, 0); @@ -135,10 +138,10 @@ public void beforeOrAt_range_returnsFromMin(){ } @Test - public void before_range_returnsFromMin(){ + public void before_range_returnsFromMin() { int maxYear = 2020; DateTimeGranularity offsetGranularity = DateTimeGranularity.create("MILLIS"); - FieldSpec fieldSpec = forYears(maxYear-3, maxYear); + FieldSpec fieldSpec = forYears(maxYear - 3, maxYear); BeforeRelation relation = new BeforeRelation(main, other, false, DateTimeDefaults.get(), offsetGranularity, 0); RestrictionsFieldSpec actualFieldSpec = (RestrictionsFieldSpec) relation.createModifierFromOtherFieldSpec(fieldSpec); @@ -154,6 +157,30 @@ public void before_range_returnsFromMin(){ assertEquals(actualMax.compareTo(expectedMax), 0); } + @Test + public void equalTo_forSet_returns() { + DistributedList expectedValues = DistributedList.uniform( + Stream.of("2", "3", "4") + .map(BigDecimal::new) + .collect(Collectors.toSet())); + WhitelistFieldSpec expected = FieldSpecFactory.fromList(expectedValues); + + DistributedList secondValues = DistributedList.uniform( + Stream.of("1", "2", "3") + .map(BigDecimal::new) + .collect(Collectors.toSet())); + WhitelistFieldSpec second = FieldSpecFactory.fromList(secondValues); + + Field firstField = FieldBuilder.createField("first"); + Field secondField = FieldBuilder.createField("second"); + + FieldSpecRelation equalTo = new EqualToOffsetRelation<>(firstField, secondField, NumericGranularity.INTEGER_DEFAULT, 1); + + FieldSpec result = equalTo.createModifierFromOtherFieldSpec(second); + + assertEquals(expected, result); + } + private FieldSpec fromMin(int year) { OffsetDateTime min = OffsetDateTime.of(year, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); LinearRestrictions restrictions = new LinearRestrictions(min, ISO_MAX_DATE, new DateTimeGranularity(MILLIS)); diff --git a/examples/integerOffsetFromSet/README.md b/examples/integerOffsetFromSet/README.md new file mode 100644 index 000000000..731926b84 --- /dev/null +++ b/examples/integerOffsetFromSet/README.md @@ -0,0 +1,3 @@ +Creates two integer fields, loaded from sets. + +The integers are linked by the condition that `Second` must be one greater than `First`. \ No newline at end of file diff --git a/examples/integerOffsetFromSet/profile.json b/examples/integerOffsetFromSet/profile.json new file mode 100644 index 000000000..44d881092 --- /dev/null +++ b/examples/integerOffsetFromSet/profile.json @@ -0,0 +1,24 @@ +{ + "fields" : [ { + "name" : "First", + "type" : "integer", + "unique" : false, + "nullable" : false + }, { + "name" : "Second", + "type" : "integer", + "unique" : false, + "nullable" : false + } ], + "constraints" : [ { + "field" : "First", + "inSet" : [1, 2, 3] + }, { + "field" : "Second", + "inSet" : [3, 4, 5] + }, { + "field" : "Second", + "equalToField" : "First", + "offset": 1 + }] +} \ No newline at end of file From c4909b836ecf3defb43ca9984a056844373042d8 Mon Sep 17 00:00:00 2001 From: Ryan Stuart Date: Mon, 2 Dec 2019 13:21:49 +0000 Subject: [PATCH 2/2] fix(#1489): Fix equal to offset for whitelist Partial fix for the ticket #1489. This fixes the equal to case, but doesn't address the before or after cases. The reason for a partial fix is due to the difficulty comparing the two different cases (equal vs before/after). Equal to is simple because all of the values can be adjusted by the offset, translating a set of values to a set of values. Before/after are more complicated, as theoretically each of the values must be translated to a range. This therefore means the result is a set of restrictions. Unfortunately, the system isn't designed to be passing around a set of restrictions at this time in the program. Therefore, this will take further thought. A possible solution is to take the most restrictive of the values. This would ensure that whichever values are picked are permissable, but would severely limit the number of values output. --- .../fieldspecs/relations/EqualToOffsetRelation.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/EqualToOffsetRelation.java b/core/src/main/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/EqualToOffsetRelation.java index 1cc3c8ced..af6b9cdf6 100644 --- a/core/src/main/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/EqualToOffsetRelation.java +++ b/core/src/main/java/com/scottlogic/datahelix/generator/core/fieldspecs/relations/EqualToOffsetRelation.java @@ -29,8 +29,7 @@ import java.util.List; import java.util.stream.Collectors; -public class EqualToOffsetRelation> implements FieldSpecRelation -{ +public class EqualToOffsetRelation> implements FieldSpecRelation { private final Field main; private final Field other; private final Granularity offsetGranularity; @@ -48,18 +47,21 @@ public EqualToOffsetRelation(Field main, @Override public FieldSpec createModifierFromOtherFieldSpec(FieldSpec otherFieldSpec) { - if (otherFieldSpec instanceof NullOnlyFieldSpec){ + if (otherFieldSpec instanceof NullOnlyFieldSpec) { return FieldSpecFactory.nullOnly(); } if (otherFieldSpec instanceof WhitelistFieldSpec) { WhitelistFieldSpec whitelistFieldSpec = (WhitelistFieldSpec) otherFieldSpec; - List> modified = whitelistFieldSpec.getWhitelist().distributedList().stream() + List> modified = whitelistFieldSpec + .getWhitelist() + .distributedList() + .stream() .map(x -> new WeightedElement<>(offsetGranularity.getNext((T) x.element(), offset), x.weight())) .collect(Collectors.toList()); return FieldSpecFactory.fromList((DistributedList) new DistributedList<>(modified)); } - LinearRestrictions otherRestrictions = (LinearRestrictions)((RestrictionsFieldSpec) otherFieldSpec).getRestrictions(); + LinearRestrictions otherRestrictions = (LinearRestrictions) ((RestrictionsFieldSpec) otherFieldSpec).getRestrictions(); if (otherRestrictions.isContradictory()) { return FieldSpecFactory.nullOnly();