package software.amazon.event.ruler; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import software.amazon.event.ruler.input.ParseException; import org.junit.Test; import static software.amazon.event.ruler.PermutationsGenerator.generateAllPermutations; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class ByteMachineTest { @Test public void WHEN_ManyOverLappingStringsAreAdded_THEN_TheyAreParsedProperly() { String[] patterns = { "a", "ab", "ab", "ab", "abc", "abx", "abz", "abzz", "x", "cafe furioso", "café", "Ξ\uD800\uDF46ज", "Ξ ज" }; ByteMachine cut = new ByteMachine(); for (String pattern : patterns) { cut.addPattern(Patterns.exactMatch(pattern)); } for (String pattern : patterns) { assertFalse(cut.transitionOn(pattern).isEmpty()); } String[] noMatches = { "foo", "bar", "baz", "cafe pacifica", "cafë" }; for (String noMatch : noMatches) { assertTrue(cut.transitionOn(noMatch).isEmpty()); } } @Test public void WHEN_AnythingButPrefixPatternIsAdded_THEN_ItMatchesAppropriately() { Patterns abpp = Patterns.anythingButPrefix("foo"); ByteMachine bm = new ByteMachine(); bm.addPattern(abpp); String[] shouldMatch = { "f", "fo", "a", "33" }; String[] shouldNotMatch = { "foo", "foo1", "foossssss2sssssssssssss4ssssssssssssss5sssssssssssssss7ssssssssssssssssssssssssssssssssssssssss" }; Set<NameStateWithPattern> matches; for (String s : shouldMatch) { matches = bm.transitionOn(s); assertEquals(1, matches.size()); } for (String s : shouldNotMatch) { matches = bm.transitionOn(s); assertEquals(0, matches.size()); } abpp = Patterns.anythingButPrefix("foo"); bm.deletePattern(abpp); for (String s : shouldMatch) { matches = bm.transitionOn(s); assertEquals(0, matches.size()); } // exercise deletePattern Set<String> abs = new HashSet<>(); abs.add("foo"); AnythingBut ab = AnythingBut.anythingButMatch(abs); bm.addPattern(abpp); Patterns[] trickyPatterns = { Patterns.prefixMatch("foo"), Patterns.anythingButPrefix("f"), Patterns.anythingButPrefix("fo"), Patterns.anythingButPrefix("fooo"), Patterns.exactMatch("foo"), ab }; for (Patterns tricky : trickyPatterns) { bm.deletePattern(tricky); } for (String s : shouldMatch) { matches = bm.transitionOn(s); assertEquals(1, matches.size()); } } @Test public void WHEN_NumericEQIsAdded_THEN_ItMatchesMultipleNumericForms() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.numericEquals(300.0)); String[] threeHundreds = { "300", "3.0e+2", "300.0000" }; for (String threeHundred : threeHundreds) { assertEquals(1, cut.transitionOn(threeHundred).size()); } } @Test public void WHEN_NumericRangesAreAdded_THEN_TheyWorkCorrectly() { double[] data = { -Constants.FIVE_BILLION, -4_999_999_999.99999, -4_999_999_999.99998, -4_999_999_999.99997, -999999999.99999, -999999999.99, -10000, -0.000002, 0, 0.000001, 3.8, 2.5e4, 999999999.999998, 999999999.999999, 1628955663d, 3206792463d, 4784629263d, 4_999_999_999.99997, 4_999_999_999.99998, 4_999_999_999.99999, Constants.FIVE_BILLION }; // Orderly add rule and random delete rules { Range[] ranges = new Range[data.length-1]; int rangeIdx = 0; ByteMachine cut = new ByteMachine(); for (int i = 1; i < data.length; i++) { cut.addPattern(Range.lessThan(data[i])); ranges[rangeIdx++] = Range.lessThan(data[i]); } // shuffle the array List<Range> list = Arrays.asList(ranges); Collections.shuffle(list); list.toArray(ranges); for (Range range : ranges) { cut.deletePattern(range); } assertTrue("byteMachine must be empty after delete pattern", cut.isEmpty()); } // first: less than for (int i = 1; i < data.length; i++) { ByteMachine cut = new ByteMachine(); cut.addPattern(Range.lessThan(data[i])); for (double aData : data) { String num = String.format("%f", aData); Set<NameStateWithPattern> matched = cut.transitionOn(num); if (aData < data[i]) { assertEquals(num + " should match < " + data[i], 1, matched.size()); } else { assertEquals(num + " should not match <" + data[i], 0, matched.size()); } } cut.deletePattern(Range.lessThan(data[i])); assertTrue("byteMachine must be empty after delete pattern", cut.isEmpty()); } // <= for (int i = 1; i < data.length; i++) { ByteMachine cut = new ByteMachine(); cut.addPattern(Range.lessThanOrEqualTo(data[i])); for (double aData : data) { String num = String.format("%f", aData); Set<NameStateWithPattern> matched = cut.transitionOn(num); if (aData <= data[i]) { assertEquals(num + " should match <= " + data[i], 1, matched.size()); } else { assertEquals(num + " should not match <=" + data[i], 0, matched.size()); } } cut.deletePattern(Range.lessThanOrEqualTo(data[i])); assertTrue("byteMachine must be empty after delete pattern", cut.isEmpty()); } // > for (int i = 0; i < (data.length - 1); i++) { ByteMachine cut = new ByteMachine(); cut.addPattern(Range.greaterThan(data[i])); for (double aData : data) { String num = String.format("%f", aData); Set<NameStateWithPattern> matched = cut.transitionOn(num); if (aData > data[i]) { assertEquals(num + " should match > " + data[i], 1, matched.size()); } else { assertEquals(num + " should not match >" + data[i], 0, matched.size()); } } cut.deletePattern(Range.greaterThan(data[i])); assertTrue("byteMachine must be empty after delete pattern", cut.isEmpty()); } // >= for (int i = 0; i < (data.length - 1); i++) { ByteMachine cut = new ByteMachine(); Range nr = Range.greaterThanOrEqualTo(data[i]); cut.addPattern(nr); for (double aData : data) { String num = String.format("%f", aData); Set<NameStateWithPattern> matched = cut.transitionOn(num); if (aData >= data[i]) { assertEquals(num + " should match > " + data[i], 1, matched.size()); } else { assertEquals(num + " should not match >" + data[i], 0, matched.size()); } } cut.deletePattern(Range.greaterThanOrEqualTo(data[i])); assertTrue("byteMachine must be empty after delete pattern", cut.isEmpty()); } // open/open range for (int i = 0; i < (data.length - 2); i++) { for (int j = i + 2; j < data.length; j++) { ByteMachine cut = new ByteMachine(); Range r = Range.between(data[i], true, data[j], true); cut.addPattern(r); for (double aData : data) { String num = String.format("%f", aData); Set<NameStateWithPattern> matched = cut.transitionOn(num); if (aData > data[i] && aData < data[j]) { assertEquals(1, matched.size()); } else { assertEquals(0, matched.size()); } } cut.deletePattern(r); assertTrue("byteMachine must be empty after delete pattern", cut.isEmpty()); } } // open/closed range for (int i = 0; i < (data.length - 2); i++) { for (int j = i + 2; j < data.length; j++) { ByteMachine cut = new ByteMachine(); Range r = Range.between(data[i], true, data[j], false); cut.addPattern(r); for (double aData : data) { String num = String.format("%f", aData); Set<NameStateWithPattern> matched = cut.transitionOn(num); if (aData > data[i] && aData <= data[j]) { assertEquals(1, matched.size()); } else { assertEquals(0, matched.size()); } } cut.deletePattern(r); assertTrue("byteMachine must be empty after delete pattern", cut.isEmpty()); } } // closed/open range for (int i = 0; i < (data.length - 2); i++) { for (int j = i + 2; j < data.length; j++) { ByteMachine cut = new ByteMachine(); Range r = Range.between(data[i], false, data[j], true); cut.addPattern(r); for (double aData : data) { String num = String.format("%f", aData); Set<NameStateWithPattern> matched = cut.transitionOn(num); if (aData >= data[i] && aData < data[j]) { assertEquals(1, matched.size()); } else { assertEquals(0, matched.size()); } } cut.deletePattern(r); assertTrue("byteMachine must be empty after delete pattern", cut.isEmpty()); } } // closed/closed range for (int i = 0; i < (data.length - 2); i++) { for (int j = i + 2; j < data.length; j++) { ByteMachine cut = new ByteMachine(); Range r = Range.between(data[i], false, data[j], false); cut.addPattern(r); for (double aData : data) { String num = String.format("%f", aData); Set<NameStateWithPattern> matched = cut.transitionOn(num); if (aData >= data[i] && aData <= data[j]) { assertEquals(1, matched.size()); } else { assertEquals(0, matched.size()); } } cut.deletePattern(r); assertTrue("byteMachine must be empty after delete pattern", cut.isEmpty()); } } // overlapping ranges int[] containedCount = new int[data.length]; ByteMachine cut = new ByteMachine(); for (int i = 0; i < (data.length - 2); i++) { for (int j = i + 2; j < data.length; j++) { Range r = Range.between(data[i], false, data[j], false); cut.addPattern(r); for (int k = 0; k < data.length; k++) { if (data[k] >= data[i] && data[k] <= data[j]) { containedCount[k]++; } } } } for (int k = 0; k < data.length; k++) { String num = String.format("%f", data[k]); Set<NameStateWithPattern> matched = cut.transitionOn(num); assertEquals(containedCount[k], matched.size()); } // delete the range for (int i = 0; i < (data.length - 2); i++) { for (int j = i + 2; j < data.length; j++) { Range r = Range.between(data[i], false, data[j], false); cut.deletePattern(r); for (int k = 0; k < data.length; k++) { if (data[k] >= data[i] && data[k] <= data[j]) { containedCount[k]--; } } } } assertTrue("byteMachine must be empty after delete pattern", cut.isEmpty()); for (int k = 0; k < data.length; k++) { String num = String.format("%f", data[k]); Set<NameStateWithPattern> matched = cut.transitionOn(num); assertEquals(containedCount[k], matched.size()); assertEquals(0, matched.size()); } // overlapping open ranges containedCount = new int[data.length]; cut = new ByteMachine(); for (int i = 0; i < (data.length - 2); i++) { for (int j = i + 2; j < data.length; j++) { Range r = Range.between(data[i], true, data[j], true); cut.addPattern(r); for (int k = 0; k < data.length; k++) { if (data[k] > data[i] && data[k] < data[j]) { containedCount[k]++; } } } } for (int k = 0; k < data.length; k++) { String num = String.format("%f", data[k]); Set<NameStateWithPattern> matched = cut.transitionOn(num); assertEquals(containedCount[k], matched.size()); } for (int i = 0; i < (data.length - 2); i++) { for (int j = i + 2; j < data.length; j++) { Range r = Range.between(data[i], true, data[j], true); cut.deletePattern(r); for (int k = 0; k < data.length; k++) { if (data[k] > data[i] && data[k] < data[j]) { containedCount[k]--; } } } } assertTrue("byteMachine must be empty after delete pattern", cut.isEmpty()); for (int k = 0; k < data.length; k++) { String num = String.format("%f", data[k]); Set<NameStateWithPattern> matched = cut.transitionOn(num); assertEquals(containedCount[k], matched.size()); assertEquals(0, matched.size()); } } @Test public void WHEN_AnExactMatchAndAPrefixMatchCoincide_THEN_TwoNameStateJumpsAreGenerated() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.exactMatch("horse")); cut.addPattern(Patterns.prefixMatch("horse")); assertEquals(2, cut.transitionOn("horse").size()); assertEquals(1, cut.transitionOn("horseback").size()); } @Test public void WHEN_TheSamePatternIsAddedTwice_THEN_ItOnlyCausesOneNamestateJump() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.exactMatch("foo")); cut.addPattern(Patterns.exactMatch("foo")); Set<NameStateWithPattern> l = cut.transitionOn("foo"); assertEquals(1, l.size()); } @Test public void WHEN_AnyThingButPatternsAreAdded_THEN_TheyWork() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.anythingButMatch("foo")); String[] notFoos = { "bar", "baz", "for", "too", "fro", "fo", "foobar" }; for (String notFoo : notFoos) { assertEquals(1, cut.transitionOn(notFoo).size()); } assertEquals(0, cut.transitionOn("foo").size()); } @Test public void WHEN_AnyThingButStringListPatternsAreAdded_THEN_TheyWork() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.anythingButMatch(new HashSet<>(Arrays.asList("bab","ford")))); String[] notFoos = { "bar", "baz", "for", "too", "fro", "fo", "foobar" }; for (String notFoo : notFoos) { assertEquals(1, cut.transitionOn(notFoo).size()); } assertEquals(0, cut.transitionOn("bab").size()); assertEquals(0, cut.transitionOn("ford").size()); } @Test public void WHEN_AnyThingButNumberListPatternsAreAdded_THEN_TheyWork() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.anythingButNumberMatch( new HashSet<>(Arrays.asList(1.11, 2d)))); cut.addPattern(Patterns.anythingButNumberMatch( new HashSet<>(Arrays.asList(3.33, 2d)))); String[] notFoos = { "0", "1.1", "5", "9", "112", "fo", "foobar" }; for (String notFoo : notFoos) { assertEquals(2, cut.transitionOn(notFoo).size()); } assertEquals(1, cut.transitionOn("1.11").size()); assertEquals(1, cut.transitionOn("3.33").size()); assertEquals(2, cut.transitionOn("1").size()); assertEquals(0, cut.transitionOn("2").size()); cut.deletePattern(Patterns.anythingButNumberMatch( new HashSet<>(Arrays.asList(1.11, 2d)))); for (String notFoo : notFoos) { assertEquals(1, cut.transitionOn(notFoo).size()); } assertEquals(1, cut.transitionOn("1.11").size()); assertEquals(0, cut.transitionOn("3.33").size()); assertEquals(1, cut.transitionOn("1").size()); assertEquals(0, cut.transitionOn("2").size()); assertFalse(cut.isEmpty()); cut.deletePattern(Patterns.anythingButNumberMatch( new HashSet<>(Arrays.asList(3.33, 2d)))); for (String notFoo : notFoos) { assertEquals(0, cut.transitionOn(notFoo).size()); } assertEquals(0, cut.transitionOn("1.11").size()); assertEquals(0, cut.transitionOn("3.33").size()); assertEquals(0, cut.transitionOn("1").size()); assertEquals(0, cut.transitionOn("2").size()); assertTrue(cut.isEmpty()); cut.addPattern(Patterns.anythingButNumberMatch( new HashSet<>(Arrays.asList(3.33, 2d)))); assertEquals(0, cut.transitionOn("2").size()); assertEquals(0, cut.transitionOn("3.33").size()); assertEquals(1, cut.transitionOn("2022").size()); assertEquals(1, cut.transitionOn("400").size()); } @Test public void WHEN_MixedPatternsAreAdded_THEN_TheyWork() { ByteMachine cut = new ByteMachine(); Patterns p = Patterns.exactMatch("foo"); cut.addPattern(p); p = Patterns.prefixMatch("foo"); cut.addPattern(p); p = Patterns.anythingButMatch("foo"); cut.addPattern(p); p = Patterns.numericEquals(3); cut.addPattern(p); p = Range.lessThan(3); cut.addPattern(p); p = Range.greaterThan(3); cut.addPattern(p); p = Range.lessThanOrEqualTo(3); cut.addPattern(p); p = Range.greaterThanOrEqualTo(3); cut.addPattern(p); p = Range.between(0, false, 8, false); cut.addPattern(p); p = Range.between(0, true, 8, true); cut.addPattern(p); String[] notFoos = { "bar", "baz", "for", "too", "fro", "fo", "foobar" }; int[] notFooMatches = { 1, 1, 1, 1, 1, 1, 2 }; for (int i = 0; i < notFooMatches.length; i++) { assertEquals(notFooMatches[i], cut.transitionOn(notFoos[i]).size()); } assertEquals(2, cut.transitionOn("foo").size()); String[] numbers = { "3", "2.5", "3.5", "0", "8", "-1", "9" }; int[] numMatches = { 6, 5, 5, 4, 4, 3, 3 } ; for (int i = 0; i < numbers.length; i++) { assertEquals("numbers[" + i + "]=" + numbers[i], numMatches[i], cut.transitionOn(numbers[i]).size()); } } @Test public void WHEN_AnythingButIsAPrefixOfAnotherPattern_THEN_TheyWork() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.exactMatch("football")); cut.addPattern(Patterns.anythingButMatch("foo")); assertEquals(2, cut.transitionOn("football").size()); assertEquals(0, cut.transitionOn("foo").size()); } @Test public void WHEN_NumericEQIsRequested_THEN_DifferentNumberSyntaxesMatch() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.numericEquals(300.0)); assertEquals(1, cut.transitionOn("300").size()); assertEquals(1, cut.transitionOn("300.0000").size()); assertEquals(1, cut.transitionOn("3.0e+2").size()); } @Test public void RangePatternEqual() { double[] data = { 1234, 5678.1234, 7890 }; NameState ns = new NameState(); ByteMatch cut = new ByteMatch(Range.lessThan(data[0]), ns); ByteMatch cc = new ByteMatch(Range.lessThan(data[1]), ns); ByteMatch s2 = new ByteMatch(Range.lessThan(data[2]), ns); Range range1 = (Range) cut.getPattern().clone(); Range range2 = (Range) cc.getPattern().clone(); Range range3 = (Range) s2.getPattern().clone(); assertEquals("pattern match range.", range1, cut.getPattern()); assertEquals("pattern match range.", range2, cc.getPattern()); assertEquals("pattern match range.", range3, s2.getPattern()); assertNotSame("pattern object doesn't match range.", range1, (cut.getPattern())); assertNotSame("pattern object doesn't match range", range2, (cc.getPattern())); assertNotSame("pattern object doesn't match range", range3, (s2.getPattern())); } @Test public void WHEN_NumericRangesAdded_THEN_TheyWorkCorrectly_THEN_MatchNothing_AFTER_Removed() { ByteMachine cut = new ByteMachine(); Range r = Range.between(0, true, 4, false); Range r1 = Range.between(1, true, 3, false); cut.addPattern(r); cut.addPattern(r1); String num = String.format("%f", 2.0); assertEquals(2, cut.transitionOn(num).size()); cut.deletePattern(r); assertEquals(1, cut.transitionOn(num).size()); cut.deletePattern(r1); assertEquals(0, cut.transitionOn(num).size()); assertTrue("byteMachine must be empty after delete pattern", cut.isEmpty()); } @Test public void WHEN_NumericRangesAddedMultipleTime_BecomeEmptyWithOneDelete() { ByteMachine cut = new ByteMachine(); String num = String.format("%f", 2.0); Range r = Range.between(0, true, 4, false); Range r1 = (Range) r.clone(); Range r2 = (Range) r1.clone(); Range r3 = (Range) r2.clone(); assertEquals(0, cut.transitionOn(num).size()); cut.addPattern(r); assertEquals(1, cut.transitionOn(num).size()); cut.addPattern(r1); cut.addPattern(r2); assertNotNull("must find the range pattern", cut.findPattern(r1)); assertEquals(cut.findPattern(r1), cut.findPattern(r3)); assertEquals(1, cut.transitionOn(num).size()); cut.deletePattern(r3); assertEquals(0, cut.transitionOn(num).size()); assertTrue("byteMachine must be empty after delete pattern", cut.isEmpty()); cut.deletePattern(r); assertTrue("byteMachine must be empty after delete pattern", cut.isEmpty()); } @Test public void WHEN_PatternAdded_THEN_ItCouldBeFound_AndReturnNullForOtherPatternSearch() { // Range pattern double[] data = { -Constants.FIVE_BILLION, -4_999_999_999.99999, -4_999_999_999.99998, -4_999_999_999.99997, -999999999.99999, -999999999.99, -10000, -0.000002, 0, 0.000001, 3.8, 2.5e4, 999999999.999998, 999999999.999999, 4_999_999_999.99997, 4_999_999_999.99998, 4_999_999_999.99999, Constants.FIVE_BILLION }; ByteMachine cut = new ByteMachine(); Range r = Range.between(0, true, 4, false); assertNull("must NOT find the range pattern", cut.findPattern(r)); cut.addPattern(r); assertNotNull("must find the range pattern", cut.findPattern(r)); for (int i = 0; i < 1000; i++) { for (int j = i + 1; j < 1001; j++) { if (i == 0 && j == 4) { assertNotNull("must find the range pattern", cut.findPattern(Range.between(i, true, j, false))); } else { assertNull("must NOT find the range pattern", cut.findPattern(Range.between(i, true, j, false))); } } } for (int i = 1; i < data.length; i++) { // first: < assertNull("must NOT find the range pattern", cut.findPattern(Range.lessThan(data[i]))); // <= assertNull("must NOT find the range pattern", cut.findPattern(Range.lessThanOrEqualTo(data[i]))); } for (int i = 0; i < data.length-1; i++) { // > assertNull("must NOT find the range pattern", cut.findPattern(Range.greaterThan(data[i]))); // >= assertNull("must NOT find the range pattern", cut.findPattern(Range.greaterThanOrEqualTo(data[i]))); } for (int i = 0; i < (data.length - 2); i++) { for (int j = i + 2; j < data.length; j++) { // open/open range assertNull("must NOT find the range pattern", cut.findPattern(Range.between(data[i], true, data[j], true))); // open/closed range assertNull("must NOT find the range pattern", cut.findPattern(Range.between(data[i], true, data[j], false))); } } for (int i = 0; i < (data.length - 2); i++) { for (int j = i + 2; j < data.length; j++) { // overlap range assertNull("must NOT find the range pattern", cut.findPattern(Range.between(data[i], true, data[j], true))); } } // other pattern cut.addPattern(Patterns.exactMatch("test")); cut.addPattern(Patterns.prefixMatch("test")); cut.addPattern(Patterns.anythingButMatch("test")); cut.addPattern(Patterns.numericEquals(1.11)); assertNotNull("must find the pattern", cut.findPattern(Patterns.exactMatch("test"))); assertNotNull("must find the pattern", cut.findPattern(Patterns.prefixMatch("test"))); assertNotNull("must find the pattern", cut.findPattern(Patterns.anythingButMatch("test"))); assertNotNull("must find the pattern", cut.findPattern(Patterns.numericEquals(1.11))); assertNull("must NOT find the pattern", cut.findPattern(Patterns.exactMatch("test1"))); assertNull("must NOT find the pattern", cut.findPattern(Patterns.prefixMatch("test1"))); assertNull("must NOT find the pattern", cut.findPattern(Patterns.anythingButMatch("test1"))); assertNull("must NOT find the pattern", cut.findPattern(Patterns.numericEquals(1.111))); cut.deletePattern(Patterns.exactMatch("test")); cut.deletePattern(Patterns.prefixMatch("test")); cut.deletePattern(Patterns.anythingButMatch("test")); cut.deletePattern(Patterns.numericEquals(1.11)); cut.deletePattern(Range.between(0, true, 4, false)); assertTrue("cut is empty", cut.isEmpty()); } @Test public void whenKeyExistencePatternAdded_itCouldBeFound_AndBecomesEmptyWithOneDelete() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.existencePatterns()); NameState stateFound; cut.deletePattern(Patterns.existencePatterns()); stateFound = cut.findPattern(Patterns.existencePatterns()); assertNull(stateFound); assertTrue(cut.isEmpty()); } @Test public void testExistencePatternFindsMatch() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.existencePatterns()); Set<NameStateWithPattern> matches = cut.transitionOn("someValue"); assertEquals(1, matches.size()); cut.deletePattern(Patterns.existencePatterns()); assertTrue(cut.isEmpty()); } @Test public void testIfExistencePatternIsNotAdded_itDoesNotFindMatch() { ByteMachine cut = new ByteMachine(); Set<NameStateWithPattern> matches = cut.transitionOn("someValue"); assertEquals(0, matches.size()); assertTrue(cut.isEmpty()); } @Test public void testExistencePattern_WithOtherPatterns_workCorrectly() { ByteMachine cut = new ByteMachine(); String val = "value"; cut.addPattern(Patterns.existencePatterns()); cut.addPattern(Patterns.exactMatch(val)); Set<NameStateWithPattern> matches = cut.transitionOn("anotherValue"); assertEquals(1, matches.size()); matches = cut.transitionOn(val); assertEquals(2, matches.size()); cut.deletePattern(Patterns.existencePatterns()); matches = cut.transitionOn("anotherValue"); assertEquals(0, matches.size()); matches = cut.transitionOn(val); assertEquals(1, matches.size()); cut.deletePattern(Patterns.exactMatch(val)); matches = cut.transitionOn(val); assertEquals(0, matches.size()); } @Test public void testNonNumericValue_DoesNotMatchNumericPattern() { ByteMachine cut = new ByteMachine(); String val = "0A,"; cut.addPattern(Range.greaterThanOrEqualTo(-1e9)); Set<NameStateWithPattern> matches = cut.transitionOn(val); assertTrue(matches.isEmpty()); } @Test public void testExistencePattern_startingWithDesignatedByteString_WithOtherPatterns_workCorrectly() { ByteMachine cut = new ByteMachine(); String val = "value"; cut.addPattern(Patterns.existencePatterns()); cut.addPattern(Patterns.exactMatch(val)); Set<NameStateWithPattern> matches = cut.transitionOn("NewValue"); assertEquals(1, matches.size()); matches = cut.transitionOn(val); assertEquals(2, matches.size()); cut.deletePattern(Patterns.existencePatterns()); matches = cut.transitionOn("NewValue"); assertEquals(0, matches.size()); matches = cut.transitionOn(val); assertEquals(1, matches.size()); cut.deletePattern(Patterns.exactMatch(val)); matches = cut.transitionOn(val); assertEquals(0, matches.size()); } @Test public void WHEN_ShortcutTransAddedAndDeleted_THEN_TheyWorkCorrectly() { ByteMachine cut = new ByteMachine(); String [] values = {"a", "ab", "abc", "abcd", "a", "ab", "abc", "abcd", "ac", "acb", "aabc", "abcdeffg"}; // add them in ordering, no proxy transition is reburied. for (String value : values) { cut.addPattern(Patterns.exactMatch(value)); } for (String value :values) { assertEquals(value + " did not have a match", 1, cut.transitionOn(value).size()); } for (String value : values) { cut.deletePattern(Patterns.exactMatch(value)); } assertTrue(cut.isEmpty()); // add them in reverse ordering, there is only one proxy transition existing. for (int i = values.length-1; i >=0; i--) { cut.addPattern(Patterns.exactMatch(values[i])); } for (String value :values) { assertEquals(1, cut.transitionOn(value).size()); } for (String value : values) { cut.deletePattern(Patterns.exactMatch(value)); } assertTrue(cut.isEmpty()); String[] copiedValues = values.clone(); for (int t = 50; t > 0; t--) { //add them in random order, it should work randomizeArray(copiedValues); for (int i = values.length - 1; i >= 0; i--) { cut.addPattern(Patterns.exactMatch(copiedValues[i])); } for (String value : values) { assertEquals(1, cut.transitionOn(value).size()); } randomizeArray(copiedValues); for (String value : copiedValues) { cut.deletePattern(Patterns.exactMatch(value)); } assertTrue(cut.isEmpty()); } // test exactly match and prefix together for (int t = 50; t > 0; t--) { //add them in random order, it should work randomizeArray(copiedValues); // add them in ordering, both exactly match and prefix match for (String value : copiedValues) { cut.addPattern(Patterns.exactMatch(value)); } randomizeArray(copiedValues); // add them in ordering, both exactly match and prefix match for (String value : copiedValues) { cut.addPattern(Patterns.prefixMatch(value)); } int[] expected = {2, 3, 4, 5, 2, 3, 4, 5, 3, 4, 3, 6}; for (int i = 0; i < values.length; i++) { assertEquals(expected[i], cut.transitionOn(values[i]).size()); } randomizeArray(copiedValues); for (String value : copiedValues) { cut.deletePattern(Patterns.exactMatch(value)); cut.deletePattern(Patterns.prefixMatch(value)); } assertTrue(cut.isEmpty()); } } private static void randomizeArray(String[] array){ Random rgen = new Random(); // Random number generator for (int i=0; i<array.length; i++) { int randomPosition = rgen.nextInt(array.length); String temp = array[i]; array[i] = array[randomPosition]; array[randomPosition] = temp; } } @Test public void testSuffixPattern() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.suffixMatch("java")); String[] shouldBeMatched = { "java", ".java", "abc.java", "jjjava", "123java" }; String[] shouldNOTBeMatched = { "vav", "javaa", "xjavax", "foo", "foojaoova" }; for (String foo : shouldBeMatched) { assertEquals(1, cut.transitionOn(foo).size()); } for (String notFoo : shouldNOTBeMatched) { assertEquals(0, cut.transitionOn(notFoo).size()); } } @Test public void testEqualsIgnoreCasePattern() { String[] noMatches = new String[] { "JAV", "jav", "ava", "AVA", "JAVAx", "javax", "xJAVA", "xjava" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("jAVa"), "java", "JAVA", "Java", "jAvA", "jAVa", "JaVa") ); } @Test public void testEqualsIgnoreCasePatternWithExactMatchAsPrefix() { String[] noMatches = new String[] { "", "ja", "JA", "JAV", "javax" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("jAVa"), "java", "jAVa", "JavA", "JAVA"), new PatternMatch(Patterns.exactMatch("ja"), "ja") ); } @Test public void testEqualsIgnoreCasePatternWithExactMatchAsPrefixLengthOneLess() { String[] noMatches = new String[] { "", "JAV", "javax" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("jAVa"), "java", "jAVa", "JavA", "JAVA"), new PatternMatch(Patterns.exactMatch("jav"), "jav") ); } @Test public void testEqualsIgnoreCasePatternNonLetterCharacters() { String[] noMatches = new String[] { "", "2#$^sS我ÅaBc", "1#%^sS我ÅaBc", "1#$^sS大ÅaBc", "1#$^sS我ÅaBc" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("1#$^sS我ÅaBc"), "1#$^sS我ÅaBc", "1#$^Ss我ÅAbC") ); } @Test public void testEqualsIgnoreCaseLowerCaseCharacterWithDifferentByteLengthForUpperCase() { String[] noMatches = new String[] { "", "12a34", "12A34" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("12â±¥34"), "12â±¥34", "12Ⱥ34") ); } @Test public void testEqualsIgnoreCaseUpperCaseCharacterWithDifferentByteLengthForLowerCase() { String[] noMatches = new String[] { "", "12a34", "12A34" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("12Ⱥ34"), "12â±¥34", "12Ⱥ34") ); } @Test public void testEqualsIgnoreCaseLowerCaseCharacterWithDifferentByteLengthForUpperCaseAtStartOfString() { String[] noMatches = new String[] { "", "a12", "A12" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("â±¥12"), "â±¥12", "Ⱥ12") ); } @Test public void testEqualsIgnoreCaseUpperCaseCharacterWithDifferentByteLengthForLowerCaseAtStartOfString() { String[] noMatches = new String[] { "", "a12", "A12" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("Ⱥ12"), "â±¥12", "Ⱥ12") ); } @Test public void testEqualsIgnoreCaseLowerCaseCharacterWithDifferentByteLengthForUpperCaseAtEndOfString() { String[] noMatches = new String[] { "", "12a", "12A" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("12â±¥"), "12â±¥", "12Ⱥ") ); } @Test public void testEqualsIgnoreCaseUpperCaseCharacterWithDifferentByteLengthForLowerCaseAtEndOfString() { String[] noMatches = new String[] { "", "12a", "12A" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("12Ⱥ"), "12â±¥", "12Ⱥ") ); } @Test public void testEqualsIgnoreCaseManyCharactersWithDifferentByteLengthForLowerCaseAndUpperCase() { String[] noMatches = new String[] { "", "ΫÌȿⱯÎΫÌΗ͂k", "ΰⱾÉΪÌΰῆK" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("ΰɀÉÎΰῆK"), "ΫÌɀⱯÎΫÌΗ͂k", "ΰⱿÉΪÌΰῆK", "ΫÌⱿⱯΪÌΫÌῆk", "ΰɀÉÎΰΗ͂K") ); } @Test public void testEqualsIgnoreCaseMiddleCharacterWithDifferentByteLengthForLowerCaseAndUpperCaseWithPrefixMatches() { String[] noMatches = new String[] { "", "a", "aa" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("abȺcd"), "abâ±¥cd", "abȺcd"), new PatternMatch(Patterns.prefixMatch("ab"), "ab", "abâ±¥", "abȺ", "abâ±¥cd", "abȺcd"), new PatternMatch(Patterns.prefixMatch("abâ±¥"), "abâ±¥", "abâ±¥cd"), new PatternMatch(Patterns.prefixMatch("abȺ"), "abȺ", "abȺcd") ); } @Test public void testEqualsIgnoreCaseLastCharacterWithDifferentByteLengthForLowerCaseAndUpperCaseWithPrefixMatches() { String[] noMatches = new String[] { "", "ab" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("abcȺ"), "abcâ±¥", "abcȺ"), new PatternMatch(Patterns.prefixMatch("abc"), "abc", "abca", "abcA", "abcâ±¥", "abcȺ"), new PatternMatch(Patterns.prefixMatch("abca"), "abca"), new PatternMatch(Patterns.prefixMatch("abcA"), "abcA") ); } @Test public void testEqualsIgnoreCaseFirstCharacterWithDifferentByteLengthForCasesWithLowerCasePrefixMatch() { String[] noMatches = new String[] { "", "â±¥", "Ⱥ", "c" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("â±¥c"), "â±¥c", "Ⱥc"), new PatternMatch(Patterns.prefixMatch("â±¥c"), "â±¥c", "â±¥cd") ); } @Test public void testEqualsIgnoreCaseFirstCharacterWithDifferentByteLengthForCasesWithUpperCasePrefixMatch() { String[] noMatches = new String[] { "", "â±¥", "Ⱥ", "c" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("Ⱥc"), "â±¥c", "Ⱥc"), new PatternMatch(Patterns.prefixMatch("Ⱥc"), "Ⱥc", "Ⱥcd") ); } @Test public void testEqualsIgnoreCaseWhereLowerAndUpperCaseAlreadyExist() { String[] noMatches = new String[] { "", "a", "b" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.prefixMatch("ab"), "ab", "abc", "abC"), new PatternMatch(Patterns.prefixMatch("AB"), "AB", "ABC", "ABc"), new PatternMatch(Patterns.equalsIgnoreCaseMatch("ab"), "ab", "AB", "Ab", "aB") ); } @Test public void testEqualsIgnoreCasePatternMultipleWithMultipleExactMatch() { String[] noMatches = new String[] { "", "he", "HEL", "hell", "HELL", "helloxx" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("hElLo"), "hello", "HELLO", "HeLlO", "hElLo"), new PatternMatch(Patterns.equalsIgnoreCaseMatch("HeLlOX"), "hellox", "HELLOX", "HeLlOx", "hElLoX"), new PatternMatch(Patterns.exactMatch("hello"), "hello"), new PatternMatch(Patterns.exactMatch("HELLO"), "HELLO"), new PatternMatch(Patterns.exactMatch("hel"), "hel") ); } @Test public void testEqualsIgnoreCaseWithExactMatchLeadingCharacterSameLowerAndUpperCase() { String[] noMatches = new String[] { "", "!", "!A", "a", "A", "b", "B" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.equalsIgnoreCaseMatch("!b"), "!b", "!B"), new PatternMatch(Patterns.exactMatch("!a"), "!a") ); } @Test public void testWildcardSingleWildcardCharacter() { testPatternPermutations( new PatternMatch(Patterns.wildcardMatch("*"), "", "*", "h", "hello") ); } @Test public void testWildcardLeadingWildcardCharacter() { String[] noMatches = new String[] { "", "ello", "hellx", "xhellx", "hell5rHGGHo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("*hello"), "hello", "hhello", "xxxhello", "*hello", "23Åzhello") ); } @Test public void testWildcardNormalPositionWildcardCharacter() { String[] noMatches = new String[] { "", "hlo", "hll", "hellol", "hel5rHGGHlo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("h*llo"), "hllo", "hello", "hxxxllo", "hel23Åzlllo") ); } @Test public void testWildcardSecondLastCharWildcardCharacter() { String[] noMatches = new String[] { "", "hell", "helox", "hellox", "hel5rHGGHe" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hel*o"), "helo", "hello", "helxxxo", "hel23Åzlllo") ); } @Test public void testWildcardTrailingWildcardCharacter() { String[] noMatches = new String[] { "", "hell", "hellx", "hellxo", "hol5rHGGHo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hello*"), "hello", "hellox", "hellooo", "hello*", "hello23Åzlllo") ); } @Test public void testWildcardMultipleWildcardCharacters() { String[] noMatches = new String[] { "", "ho", "heeo", "helx", "llo", "hex5rHGGHo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("h*l*o"), "hlo", "helo", "hllo", "hloo", "hello", "hxxxlxxxo", "h*l*o", "hel*o", "h*llo", "hel23Åzlllo") ); } @Test public void testWildcardLastCharAndThirdLastCharWildcardCharacters() { String[] noMatches = new String[] { "", "he", "hex", "hexxx" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*l*"), "hel", "hexl", "helx", "helxx", "helxl", "helxlx", "helxxl", "helxxlxx", "helxxlxxl") ); } @Test public void testWildcardLastCharAndThirdLastCharWildcardCharactersForStringOfLengthThree() { String[] noMatches = new String[] { "", "x", "xx", "xtx" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("*l*"), "l", "xl", "lx", "xlx", "xxl", "lxx", "xxlxx", "xlxlxlxlxl", "lxlxlxlxlx") ); } @Test public void testWildcardEscapedAsterisk() { String[] noMatches = new String[] { "helo", "hello" }; testPatternPermutations(noMatches, // Only one backslash in the actual string. Need to escape the backslash for Java compiler. new PatternMatch(Patterns.wildcardMatch("hel\\*o"), "hel*o") ); } @Test public void testWildcardEscapedAsteriskFollowedByWildcard() { String[] noMatches = new String[] { "heo", "helo", "hello", "he*l" }; testPatternPermutations(noMatches, // Only one backslash in the actual string. Need to escape the backslash for Java compiler. new PatternMatch(Patterns.wildcardMatch("he\\**o"), "he*o", "he*llo", "he*hello") ); } @Test public void testWildcardEscapedBackslash() { String[] noMatches = new String[] { "hello", "he\\\\llo" }; testPatternPermutations(noMatches, // Only two backslashes in the actual string. Need to escape the backslashes for Java compiler. new PatternMatch(Patterns.wildcardMatch("he\\\\llo"), "he\\llo") ); } @Test public void testWildcardEscapedBackslashFollowedByEscapedAsterisk() { String[] noMatches = new String[] { "hello", "he\\\\llo", "he\\llo", "he\\xxllo" }; testPatternPermutations(noMatches, // Only three backslashes in the actual string. Need to escape the backslashes for Java compiler. new PatternMatch(Patterns.wildcardMatch("he\\\\\\*llo"), "he\\*llo") ); } @Test public void testWildcardEscapedBackslashFollowedByWildcard() { String[] noMatches = new String[] { "hello", "he\\ll" }; testPatternPermutations(noMatches, // Only two backslashes in the actual string. Need to escape the backslashes for Java compiler. new PatternMatch(Patterns.wildcardMatch("he\\\\*llo"), "he\\llo", "he\\*llo", "he\\\\llo", "he\\\\\\llo", "he\\xxllo") ); } @Test public void testWildcardInvalidEscapeCharacter() { try { new ByteMachine().addPattern(Patterns.wildcardMatch("he\\llo")); fail("Expected ParseException"); } catch (ParseException e) { assertEquals("Invalid escape character at pos 2", e.getMessage()); } } @Test public void testWildcardSingleBackslashAllowedByExactMatch() { String[] noMatches = new String[] { "hello", "he\\\\llo" }; testPatternPermutations(noMatches, // Only one backslash in the actual string. Need to escape the backslash for Java compiler. new PatternMatch(Patterns.exactMatch("he\\llo"), "he\\llo") ); } @Test public void testWildcardSingleWildcardCharacterWithOtherPatterns() { testPatternPermutations( new PatternMatch(Patterns.wildcardMatch("*"), "", "*", "h", "ho", "hello"), new PatternMatch(Patterns.wildcardMatch("h*o"), "ho", "hello"), new PatternMatch(Patterns.exactMatch("hello"), "hello") ); } @Test public void testWildcardLeadingWildcardCharacterNotUsedByExactMatch() { String[] noMatches = new String[] { "", "hello", "hellox", "blahabc" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("*hello"), "hello", "xhello", "hehello"), new PatternMatch(Patterns.exactMatch("abc"), "abc") ); } @Test public void testWildcardTwoPatternsFirstLeadingSecondNormalPositionAdjacent() { String[] noMatches = new String[] { "", "h", "ello", "hel", "hlo", "hell" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("*hello"), "hello", "xhello", "hehello"), new PatternMatch(Patterns.wildcardMatch("h*llo"), "hllo", "hello", "hehello") ); } @Test public void testWildcardTwoPatternsFirstLeadingSecondNormalPositionNonAdjacent() { String[] noMatches = new String[] { "", "h", "ello", "hel", "heo", "hell" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("*hello"), "hello", "xhello", "hehello"), new PatternMatch(Patterns.wildcardMatch("he*lo"), "helo", "hello", "hehello") ); } @Test public void testWildcardTwoPatternsFirstLeadingSecondLastCharAndThirdLastCharAdjacent() { String[] noMatches = new String[] { "", "e", "l", "lo", "hel" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("*elo"), "elo", "helo", "xhelo"), new PatternMatch(Patterns.wildcardMatch("e*l*"), "el", "elo", "exl", "elx", "exlx", "exxl", "elxx", "exxlxx") ); } @Test public void testWildcardTwoPatternsFirstLeadingSecondLastCharAndThirdLastCharNonAdjacent() { String[] noMatches = new String[] { "", "he", "hexxo", "ello" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("*hello"), "hello", "xhello", "xxhello"), new PatternMatch(Patterns.wildcardMatch("he*l*"), "hel", "hello", "helo", "hexl", "hexlx", "hexxl", "helxx", "hexxlxx") ); } @Test public void testWildcardTwoPatternsFirstNormalPositionSecondNormalPositionAdjacent() { String[] noMatches = new String[] { "", "hlo", "heo", "hllol", "helol" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("h*llo"), "hllo", "hello", "hxxxllo", "hexxxllo"), new PatternMatch(Patterns.wildcardMatch("he*lo"), "helo", "hello", "hexxxlo", "hexxxllo") ); } @Test public void testWildcardTwoPatternsFirstNormalPositionSecondNormalPositionNonAdjacent() { String[] noMatches = new String[] { "", "hlox", "hllo", "helo", "heox", "helx", "hellx", "helloxx", "heloxx" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("h*llox"), "hllox", "hellox", "hxxxllox", "helhllox", "hheloxllox"), new PatternMatch(Patterns.wildcardMatch("hel*ox"), "helox", "hellox", "helxxxox", "helhllox", "helhlloxox") ); } @Test public void testWildcardTwoPatternsFirstNormalPositionSecondLastCharAndThirdLastCharAdjacent() { String[] noMatches = new String[] { "", "h", "he", "hl", "el", "hlo", "llo", "hllol", "hxll", "hexxx" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("h*llo"), "hllo", "hello", "hxxxllo", "hexxxllo", "hexxxlllo"), new PatternMatch(Patterns.wildcardMatch("he*l*"), "hel", "helo", "hexl", "hello", "helol", "hexxxlo", "hexxxllo", "hexxxlllo") ); } @Test public void testWildcardTwoPatternsFirstNormalPositionSecondLastCharAndThirdLastCharNonAdjacent() { String[] noMatches = new String[] { "", "h", "hex", "hl", "exl", "hxlo", "xllo", "hxllol", "hxxll", "hexxx" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("h*xllo"), "hxllo", "hexllo", "hxxxllo", "hexxxllo"), new PatternMatch(Patterns.wildcardMatch("hex*l*"), "hexl", "hexlo", "hexxl", "hexllo", "hexlol", "hexxxlo", "hexxxllo", "hexxxlllo") ); } @Test public void testWildcardTwoPatternsFirstNormalPositionSecondSecondLastCharAdjacent() { String[] noMatches = new String[] { "", "hel", "heo", "hlo", "hellxox" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*lo"), "helo", "hello", "hexxxlo", "helxxxlo"), new PatternMatch(Patterns.wildcardMatch("hel*o"), "helo", "hello", "hellxo", "helxxxo", "helxxxlo") ); } @Test public void testWildcardTwoPatternsFirstNormalPositionSecondSecondLastCharNonAdjacent() { String[] noMatches = new String[] { "", "hlo", "hll", "hel", "helox" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("h*llo"), "hllo", "hello", "hxxxllo", "helllo"), new PatternMatch(Patterns.wildcardMatch("hel*o"), "helo", "hello", "helxo", "helllo") ); } @Test public void testWildcardTwoPatternsFirstNormalPositionSecondTrailing() { String[] noMatches = new String[] { "", "he", "hel", "helox", "helx", "hxlo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*lo"), "helo", "hello", "helllo", "helxlo"), new PatternMatch(Patterns.wildcardMatch("hell*"), "hell", "hello", "helllo", "hellx", "hellxxx") ); } @Test public void testWildcardTwoPatternsFirstSecondLastCharSecondTrailing() { String[] noMatches = new String[] { "", "hel", "helox", "helxox", "hexo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hel*o"), "helo", "hello", "helllo", "hellloo", "helloo", "heloo"), new PatternMatch(Patterns.wildcardMatch("hell*"), "hell", "hello", "helllo", "hellloo", "helloo", "hellox") ); } @Test public void testWildcardTwoPatternsBothTrailing() { String[] noMatches = new String[] { "", "he", "hex", "hexlo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hel*"), "hel", "helx", "hello", "hellox"), new PatternMatch(Patterns.wildcardMatch("hello*"), "hello", "hellox") ); } @Test public void testWildcardLeadingWildcardOccursBeforeLeadingCharacterOfExactMatch() { String[] noMatches = new String[] { "", "ello", "hell" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("*hello"), "hello", "hhello", "hhhello"), new PatternMatch(Patterns.exactMatch("hello"), "hello") ); } @Test public void testWildcardNormalPositionWildcardOccursBeforeNormalPositionCharacterOfExactMatch() { String[] noMatches = new String[] { "", "he", "hel", "heo", "heloz", "hellox", "heloxo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*lo"), "helo", "hello", "helllo"), new PatternMatch(Patterns.exactMatch("helox"), "helox") ); } @Test public void testWildcardSecondLastCharWildcardOccursBeforeNormalPositionCharacterOfExactMatch() { String[] noMatches = new String[] { "", "he", "helx", "helo", "hexlx", "hellox", "heloxx" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*l"), "hel", "hexl", "hexxxl"), new PatternMatch(Patterns.exactMatch("helox"), "helox") ); } @Test public void testWildcardTrailingWildcardOccursBeforeNormalPositionCharacterOfExactMatch() { String[] noMatches = new String[] { "", "h", "hxlox", "hxelox" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*"), "he", "helo", "helox", "heloxx"), new PatternMatch(Patterns.exactMatch("helox"), "helox") ); } @Test public void testWildcardMultipleWildcardOccursBeforeNormalPositionCharacterOfExactMatch() { String[] noMatches = new String[] { "", "h", "he", "hel", "hexxo", "hexxohexxo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("h*l*o"), "hlo", "helo", "hllo", "hello", "hexloo", "hellohello", "hellohellxo"), new PatternMatch(Patterns.exactMatch("hellohello"), "hellohello") ); } @Test public void testWildcardLastCharAndThirdLastCharWildcardOccursBeforeNormalPositionCharacterOfExactMatch() { String[] noMatches = new String[] { "", "h", "he", "hlo", "hexxo", "hexxohexxo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*l*"), "hel", "helo", "hexl", "hello", "hexloo", "hellohellx", "hellohello"), new PatternMatch(Patterns.exactMatch("hellohello"), "hellohello") ); } @Test public void testWildcardLeadingWildcardOccursBeforeSameFinalCharacterOfExactMatch() { String[] noMatches = new String[] { "", "x", "hx" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("*h"), "h", "hh", "xhh"), new PatternMatch(Patterns.exactMatch("h"), "h") ); } @Test public void testWildcardNormalPositionWildcardOccursBeforeSameFinalCharacterOfExactMatch() { String[] noMatches = new String[] { "", "hel", "helx", "heoo", "helxoxo", "heloxo", "helxoox" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hel*oo"), "heloo", "helloo", "helxxoo"), new PatternMatch(Patterns.exactMatch("helo"), "helo") ); } @Test public void testWildcardNormalPositionWildcardOccursBeforeDivergentFinalCharacterOfExactMatch() { String[] noMatches = new String[] { "", "hel", "helo", "heoo", "helxoxo", "heloxo", "helxoox" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hel*oo"), "heloo", "helloo", "helxxoo"), new PatternMatch(Patterns.exactMatch("helx"), "helx") ); } @Test public void testWildcardSecondLastCharWildcardOccursBeforeSameFinalCharacterOfExactMatch() { String[] noMatches = new String[] { "", "hel", "hexlo", "helox" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hel*o"), "helo", "hello", "helxxxo"), new PatternMatch(Patterns.exactMatch("helo"), "helo") ); } @Test public void testWildcardSecondLastCharWildcardOccursBeforeDivergentFinalCharacterOfExactMatch() { String[] noMatches = new String[] { "", "hel", "hexl", "helo", "helxx" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hel*o"), "helo", "hello", "helxo", "helxxxo"), new PatternMatch(Patterns.exactMatch("helx"), "helx") ); } @Test public void testWildcardTrailingWildcardOccursBeforeFinalCharacterOfExactMatch() { String[] noMatches = new String[] { "", "hex", "hexl" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hel*"), "hel", "helo", "hello", "heloo" ), new PatternMatch(Patterns.exactMatch("helo"), "helo") ); } @Test public void testWildcardMultipleWildcardOccursBeforeSameFinalCharacterOfExactMatch() { String[] noMatches = new String[] { "", "ho", "hell", "ello", "hxo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("h*l*o"), "hlo", "helo", "hllo", "hello", "hxxlo", "hlxxo", "hxxlxxo"), new PatternMatch(Patterns.exactMatch("hello"), "hello") ); } @Test public void testWildcardMultipleWildcardOccursBeforeDivergentFinalCharacterOfExactMatch() { String[] noMatches = new String[] { "", "ho", "hell", "ello", "hxo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("h*l*o"), "hlo", "helo", "hllo", "hello", "hxxlo", "hlxxo", "hxxlxxo"), new PatternMatch(Patterns.exactMatch("hellx"), "hellx") ); } @Test public void testWildcardLastCharAndThirdLastCharWildcardOccursBeforeFinalCharacterOfExactMatch() { String[] noMatches = new String[] { "", "h", "l", "elo", "hex", "hexo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("h*l*"), "hl", "hel", "hlo", "helo", "heel", "hloo", "heelo", "heloo", "heeloo" ), new PatternMatch(Patterns.exactMatch("helo"), "helo") ); } @Test public void testWildcardLeadingWildcardOccursAtDivergentCharacterOfExactMatch() { String[] noMatches = new String[] { "", "ello", "hellobye", "bbye" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("*hello"), "hello", "xhello", "byehello"), new PatternMatch(Patterns.exactMatch("bye"), "bye") ); } @Test public void testWildcardNormalPositionWildcardOccursAtDivergentCharacterOfExactMatch() { String[] noMatches = new String[] { "", "hel", "heo", "hexo", "hexzz" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*lo"), "helo", "hello", "hexxxlo"), new PatternMatch(Patterns.exactMatch("hexz"), "hexz") ); } @Test public void testWildcardSecondLastCharWildcardOccursAtDivergentCharacterOfExactMatch() { String[] noMatches = new String[] { "", "hel", "heloxz", "helxzz" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hel*o"), "helo", "heloo", "hello", "helloo", "helxxxo", "helxzo"), new PatternMatch(Patterns.exactMatch("helxz"), "helxz") ); } @Test public void testWildcardTrailingWildcardOccursAtDivergentCharacterOfExactMatch() { String[] noMatches = new String[] { "", "he", "hex" , "hexzz" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hel*"), "hel", "helx", "helxz", "hello"), new PatternMatch(Patterns.exactMatch("helxz"), "helxz") ); } @Test public void testWildcardMultipleWildcardOccursAtDivergentCharacterOfExactMatch() { String[] noMatches = new String[] { "", "ho", "hxl", "bye", "lbye", "hbye" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("h*l*o"), "hlo", "helo", "hllo", "hello", "hxxlo", "hlxxo", "hxxlxxo", "hlbyeo"), new PatternMatch(Patterns.exactMatch("hlbye"), "hlbye") ); } @Test public void testWildcardLastCharAndThirdLastCharWildcardOccursAtDivergentCharacterOfExactMatch() { String[] noMatches = new String[] { "", "ho", "hxl", "bye", "lbye", "hbye" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*l*"), "hel", "hexl", "helo", "hello", "hexxl", "helxx", "helbye", "hellobye", "helbyeo"), new PatternMatch(Patterns.exactMatch("helbye"), "helbye") ); } @Test public void testWildcardNormalPositionWildcardOccursInMiddleOfExactMatch() { String[] noMatches = new String[] { "", "he", "hel", "hellx", "hexllo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*ll"), "hell", "hexll", "hexxll"), new PatternMatch(Patterns.exactMatch("hello"), "hello") ); } @Test public void testWildcardSecondLastCharWildcardOccursInMiddleOfExactMatch() { String[] noMatches = new String[] { "", "hxl", "hxel", "hex", "hexlx" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*l"), "hel", "hexl", "hexyzl"), new PatternMatch(Patterns.exactMatch("hello"), "hello") ); } @Test public void testWildcardTrailingWildcardOccursInMiddleOfExactMatch() { String[] noMatches = new String[] { "", "hex", "hexl" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hel*"), "hel", "helx", "helhel", "hello", "hellox"), new PatternMatch(Patterns.exactMatch("hello"), "hello") ); } @Test public void testWildcardNormalPositionWildcardOccursAfterFinalCharacterOfExactMatch() { String[] noMatches = new String[] { "", "hell", "hexlo", "helxo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hel*lo"), "hello", "helllllo", "helxxxlo"), new PatternMatch(Patterns.exactMatch("hel"), "hel") ); } @Test public void testWildcardSecondLastCharWildcardOccursAfterFinalCharacterOfExactMatch() { String[] noMatches = new String[] { "", "he", "heo", "heloox" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hel*o"), "helo", "heloo", "hello", "helxo", "helllllxlllo"), new PatternMatch(Patterns.exactMatch("hel"), "hel") ); } @Test public void testWildcardTrailingWildcardOccursAfterFinalCharacterOfExactMatch() { String[] noMatches = new String[] { "", "he", "hex", "hexl" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hel*"), "hel", "hello"), new PatternMatch(Patterns.exactMatch("hel"), "hel") ); } @Test public void testWildcardMultipleWildcardOccursAfterFinalCharacterOfExactMatch() { String[] noMatches = new String[] { "", "x", "ho", "hxo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("h*l*o"), "hlo", "helo", "hllo", "hello", "hxxlxxo"), new PatternMatch(Patterns.exactMatch("h"), "h") ); } @Test public void testWildcardLastCharAndThirdLastCharWildcardOccursAfterFinalCharacterOfExactMatch() { String[] noMatches = new String[] { "", "h", "hl", "hex" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*l*"), "hel", "hexl", "helo", "hello", "hexxlxx"), new PatternMatch(Patterns.exactMatch("he"), "he") ); } @Test public void testWildcardMultipleWildcardOccursSurroundingFinalCharacterOfExactMatch() { String[] noMatches = new String[] { "", "he", "hl", "ho", "hxo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("h*l*o"), "hlo", "helo", "hllo", "hello", "hxxlxxo"), new PatternMatch(Patterns.exactMatch("hel"), "hel") ); } @Test public void testWildcardLastCharAndThirdLastCharWildcardOccursSurroundingFinalCharacterOfExactMatch() { String[] noMatches = new String[] { "", "he", "hl", "hexo", "el" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*l*"), "hel", "hell", "helo", "hello", "hexxlxx"), new PatternMatch(Patterns.exactMatch("hell"), "hell") ); } @Test public void testWildcardExactMatchHasWildcardCharacter() { String[] noMatches = new String[] { "hel", "heo", "hlo", "helox", "hxlo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*lo"), "helo", "he*lo", "hello", "hellllllo"), new PatternMatch(Patterns.exactMatch("he*lo"), "he*lo") ); } @Test public void testWildcardFourMultipleWildcardPatternsInterweaving() { String[] noMatches = new String[] { "", "ab", "bc", "ac", "xbyazc", "xbycza", "xcybza" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("*a*b*c"), "abc", "aabbcc", "xaybzc", "xxayybzzc"), new PatternMatch(Patterns.wildcardMatch("a*b*c*"), "abc", "aabbcc", "axbycz", "azzbyyczz"), new PatternMatch(Patterns.wildcardMatch("*a*c*b"), "acb", "aaccbb", "xayczb", "xxayyczzb"), new PatternMatch(Patterns.wildcardMatch("a*c*b*"), "acb", "aaccbb", "axcybz", "axxcyybzz") ); } @Test public void testWildcardWithAnythingButPattern() { testPatternPermutations( new PatternMatch(Patterns.wildcardMatch("he*lo"), "helo", "hello", "hexxxlo"), new PatternMatch(Patterns.anythingButMatch("hello"), "", "helo", "helol", "hexxxlo") ); } @Test public void testWildcardWithAnythingButSetPattern() { testPatternPermutations( new PatternMatch(Patterns.wildcardMatch("he*lo"), "helo", "hello", "hexxxlo"), new PatternMatch(Patterns.anythingButMatch(new HashSet<>(Arrays.asList("helo", "hello"))), "", "helol", "hexxxlo") ); } @Test public void testWildcardWithAnythingButPrefixPatternWildcardStartsWithPrefix() { String[] noMatches = new String[] { "he", "hel", "hell", "hexxx" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*lo"), "helo", "hello", "hellllo"), new PatternMatch(Patterns.anythingButPrefix("he"), "", "h", "hllo", "x", "xx") ); } @Test public void testWildcardWithAnythingButSuffixPatternEndsWithSuffix() { String[] noMatches = new String[] { "going", "leaving", "ng", "gong" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.anythingButSuffix("ng"), "", "g", "gog", "x", "xx") ); } @Test public void testWildcardWithAnythingButPrefixPatternWildcardStartsWithHalfOfPrefix() { String[] noMatches = new String[] { "he", "hel", "hell", "hello", "hexxx" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hx*lo"), "hxlo", "hxllo", "hxllllo"), new PatternMatch(Patterns.anythingButPrefix("he"), "", "h", "hxlo", "hxllo", "hxllllo", "x", "xx") ); } @Test public void testWildcardWithAnythingButPrefixPatternWildcardStartsWithNoneOfPrefix() { String[] noMatches = new String[] { "xe", "xex", "xexx" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hx*lo"), "hxlo", "hxllo", "hxllllo"), new PatternMatch(Patterns.anythingButPrefix("xe"), "", "x", "xyyy", "h", "he", "hxlo", "hxllo", "hxllllo") ); } @Test public void testWildcardWithPrefixPatternWildcardStartsWithPrefix() { String[] noMatches = new String[] { "", "hlo", "elo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*lo"), "helo", "hello", "hellllo"), new PatternMatch(Patterns.prefixMatch("he"), "he", "helo", "hello", "hellllo") ); } @Test public void testWildcardWithPrefixPatternWildcardStartsWithHalfOfPrefix() { String[] noMatches = new String[] { "", "hx", "hxl", "hlo", "elo", "xlo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hx*lo"), "hxlo", "hxllo", "hxllllo"), new PatternMatch(Patterns.prefixMatch("he"), "he", "helo", "hello", "hellllo") ); } @Test public void testWildcardWithPrefixPatternWildcardStartsWithNoneOfPrefix() { String[] noMatches = new String[] { "", "h", "x", "xx", "xxl", "xlo", "hxllo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("xx*lo"), "xxlo", "xxllo", "xxllllo"), new PatternMatch(Patterns.prefixMatch("he"), "he", "helo", "hello", "hellllo") ); } @Test public void testWildcardWithSuffixPatternWildcardEndsWithSuffix() { String[] noMatches = new String[] { "", "he", "hel" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*lo"), "helo", "hello", "hellllo"), new PatternMatch(Patterns.suffixMatch("lo"), "lo", "helo", "hello", "hellllo") ); } @Test public void testWildcardWithSuffixPatternWildcardEndsWithHalfOfSuffix() { String[] noMatches = new String[] { "", "he", "hex", "exo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*xo"), "hexo", "hexxo", "hexxxxo"), new PatternMatch(Patterns.suffixMatch("lo"), "lo", "helo", "hello", "hellllo") ); } @Test public void testWildcardWithSuffixPatternWildcardEndsWithNoneOfSuffix() { String[] noMatches = new String[] { "", "he", "hex", "exy", "exo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*xy"), "hexy", "hexxy", "hexxxxy"), new PatternMatch(Patterns.suffixMatch("lo"), "lo", "helo", "hello", "hellllo") ); } @Test public void testWildcardWithEqualsIgnoreCasePattern() { String[] noMatches = new String[] { "", "hel", "heo", "HEXLO", "HExLO", "hexlO", "helllLo", "hElo", "HEXlo" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("he*lo"), "helo", "hello", "heLlo", "hellllo"), new PatternMatch(Patterns.equalsIgnoreCaseMatch("heLLo"), "heLLo", "hello", "HELLO", "HEllO", "HeLlO", "hElLo", "heLlo") ); } @Test public void testWildcardWithExistencePattern() { testPatternPermutations( new PatternMatch(Patterns.wildcardMatch("he*lo"), "helo", "hello", "hellllo"), new PatternMatch(Patterns.existencePatterns(), "", "a", "b", "c", "abc", "helo", "hello", "hellllo") ); } @Test public void testWildcardSecondWildcardCharacterIsNotReusedByOtherWildcardRule() { String[] noMatches = new String[] { "", "abcd", "bcde", "ae", "xabcde", "abcdex" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("a*bc*de"), "abcde", "axbcde", "abcxde", "axbcxde"), new PatternMatch(Patterns.wildcardMatch("a*bcde"), "abcde", "axbcde") ); } @Test public void testWildcardAddTwiceDeleteOnceLeadingWildcard() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.wildcardMatch("*hello")); cut.addPattern(Patterns.wildcardMatch("*hello")); cut.deletePattern(Patterns.wildcardMatch("*hello")); assertTrue(cut.isEmpty()); } @Test public void testWildcardAddTwiceDeleteOnceNormalPositionWildcard() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.wildcardMatch("h*llo")); cut.addPattern(Patterns.wildcardMatch("h*llo")); cut.deletePattern(Patterns.wildcardMatch("h*llo")); assertTrue(cut.isEmpty()); } @Test public void testWildcardAddTwiceDeleteOnceSecondLastCharWildcard() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.wildcardMatch("hell*o")); cut.addPattern(Patterns.wildcardMatch("hell*o")); cut.deletePattern(Patterns.wildcardMatch("hell*o")); assertTrue(cut.isEmpty()); } @Test public void testWildcardAddTwiceDeleteOnceTrailingWildcard() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.wildcardMatch("hello*")); cut.addPattern(Patterns.wildcardMatch("hello*")); cut.deletePattern(Patterns.wildcardMatch("hello*")); assertTrue(cut.isEmpty()); } @Test public void testWildcardAddTwiceDeleteOnceMultipleWildcard() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.wildcardMatch("h*l*o")); cut.addPattern(Patterns.wildcardMatch("h*l*o")); cut.deletePattern(Patterns.wildcardMatch("h*l*o")); assertTrue(cut.isEmpty()); } @Test public void testWildcardAddTwiceDeleteOnceLastCharAndThirdLastCharWildcard() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.wildcardMatch("he*l*")); cut.addPattern(Patterns.wildcardMatch("he*l*")); cut.deletePattern(Patterns.wildcardMatch("he*l*")); assertTrue(cut.isEmpty()); } @Test public void testWildcardAddTwiceDeleteOnceSingleCharWildcard() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.wildcardMatch("*")); cut.addPattern(Patterns.wildcardMatch("*")); cut.deletePattern(Patterns.wildcardMatch("*")); assertTrue(cut.isEmpty()); } @Test public void testWildcardAddTwiceDeleteOnceMixed() { ByteMachine cut = new ByteMachine(); for (int i = 0; i < 2; i ++){ cut.addPattern(Patterns.wildcardMatch("h*llo")); cut.addPattern(Patterns.wildcardMatch("*")); cut.addPattern(Patterns.wildcardMatch("*hello")); cut.addPattern(Patterns.wildcardMatch("hello*")); cut.addPattern(Patterns.wildcardMatch("hell*o")); cut.addPattern(Patterns.wildcardMatch("h*l*o")); cut.addPattern(Patterns.wildcardMatch("he*l*")); } cut.deletePattern(Patterns.wildcardMatch("h*llo")); cut.deletePattern(Patterns.wildcardMatch("*")); cut.deletePattern(Patterns.wildcardMatch("*hello")); cut.deletePattern(Patterns.wildcardMatch("hello*")); cut.deletePattern(Patterns.wildcardMatch("hell*o")); cut.deletePattern(Patterns.wildcardMatch("h*l*o")); cut.deletePattern(Patterns.wildcardMatch("he*l*")); assertTrue(cut.isEmpty()); } @Test public void testWildcardMachineNotEmptyWithSingleWildcardCharacterPattern() { ByteMachine cut = new ByteMachine(); cut.addPattern(Patterns.wildcardMatch("*")); assertFalse(cut.isEmpty()); cut.deletePattern(Patterns.wildcardMatch("*")); assertTrue(cut.isEmpty()); } @Test public void testWildcardRuleIsNotDuplicated() { ByteMachine cut = new ByteMachine(); for (int i = 0; i < 10; i++) { cut.addPattern(Patterns.wildcardMatch("1*2345")); } assertEquals(2, cut.evaluateComplexity(new MachineComplexityEvaluator(Integer.MAX_VALUE))); } @Test public void testWildcardRuleIsNotDuplicatedWildcardIsFirstChar() { ByteMachine cut = new ByteMachine(); for (int i = 0; i < 10; i++) { cut.addPattern(Patterns.wildcardMatch("*123")); } assertEquals(2, cut.evaluateComplexity(new MachineComplexityEvaluator(Integer.MAX_VALUE))); } @Test public void testWildcardRuleIsNotDuplicatedWildcardIsSecondLastChar() { ByteMachine cut = new ByteMachine(); for (int i = 0; i < 10; i++) { cut.addPattern(Patterns.wildcardMatch("1*2")); } assertEquals(2, cut.evaluateComplexity(new MachineComplexityEvaluator(Integer.MAX_VALUE))); } @Test public void testWildcardRuleIsNotDuplicatedWildcardIsLastChar() { ByteMachine cut = new ByteMachine(); for (int i = 0; i < 10; i++) { cut.addPattern(Patterns.wildcardMatch("1*")); } assertEquals(2, cut.evaluateComplexity(new MachineComplexityEvaluator(Integer.MAX_VALUE))); } @Test public void testWildcardRuleIsNotDuplicatedWildcardIsThirdLastAndLastChar() { ByteMachine cut = new ByteMachine(); for (int i = 0; i < 10; i++) { cut.addPattern(Patterns.wildcardMatch("12*3*")); } assertEquals(3, cut.evaluateComplexity(new MachineComplexityEvaluator(Integer.MAX_VALUE))); } @Test public void testWildcardMultipleWildcardPatterns1() { // Considering the following as potential matches: "hello", "hxxllo", "xhello", "xxhello", "hellox", "helloxx", "hellxo", "hellxxo", "", "helxlo", "kaboom" String[] noMatches = new String[] { "", "helxlo", "kaboom" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("h*llo"), "hello", "hxxllo"), new PatternMatch(Patterns.wildcardMatch("*hello"), "hello", "xhello", "xxhello"), new PatternMatch(Patterns.wildcardMatch("hello*"), "hello", "hellox", "helloxx"), new PatternMatch(Patterns.wildcardMatch("hell*o"), "hello", "hellxo", "hellxxo") ); } @Test public void testWildcardMultipleWildcardPatterns2() { // Considering the following as potential matches: "hello", "hellox", "hxllo", "hxlxo", "xlo", "", "hell", "hellx", "xell", "xellox" String[] noMatches = new String[] { "", "hell", "hellx", "xell", "xellox" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("hello*"), "hello", "hellox"), new PatternMatch(Patterns.wildcardMatch("h*llo"), "hello", "hxllo"), new PatternMatch(Patterns.wildcardMatch("h*l*o"), "hello", "hxllo", "hxlxo"), new PatternMatch(Patterns.wildcardMatch("*lo"), "hello", "hxllo", "xlo") ); } @Test public void testWildcardMultipleMixedPatterns() { // Considering the following as potential matches: "", "x", "hello", "xhello", "helo", "helxo", "hellox", "", "x", "xello" String[] noMatches = new String[] { "", "x", "xello" }; testPatternPermutations(noMatches, new PatternMatch(Patterns.wildcardMatch("*hello"), "hello", "xhello"), new PatternMatch(Patterns.wildcardMatch("hel*o"), "hello", "helo", "helxo"), new PatternMatch(Patterns.wildcardMatch("hello*"), "hello", "hellox"), new PatternMatch(Patterns.wildcardMatch("h*o"), "hello", "helo", "helxo"), new PatternMatch(Patterns.exactMatch("hello"), "hello"), new PatternMatch(Patterns.exactMatch("helo"), "helo"), new PatternMatch(Patterns.prefixMatch("h"), "hello", "helo", "helxo", "hellox") ); } @Test public void testWildcardNoConsecutiveWildcardCharacters() { try { new ByteMachine().addPattern(Patterns.wildcardMatch("h**o")); fail("Expected ParseException"); } catch (ParseException e) { assertEquals("Consecutive wildcard characters at pos 1", e.getMessage()); } } @Test public void testAddMatchPatternGivenNameStateReturned() { NameState nameState = new NameState(); ByteMachine cut = new ByteMachine(); assertSame(nameState, cut.addPattern(Patterns.exactMatch("a"), nameState)); } @Test public void testAddMatchPatternNoNameStateGiven() { ByteMachine cut = new ByteMachine(); assertNotNull(cut.addPattern(Patterns.exactMatch("a"))); } @Test public void testAddExistencePatternGivenNameStateReturned() { NameState nameState = new NameState(); ByteMachine cut = new ByteMachine(); assertSame(nameState, cut.addPattern(Patterns.existencePatterns(), nameState)); } @Test public void testAddExistencePatternNoNameStateGiven() { ByteMachine cut = new ByteMachine(); assertNotNull(cut.addPattern(Patterns.existencePatterns())); } @Test public void testAddAnythingButPatternGivenNameStateReturned() { NameState nameState = new NameState(); ByteMachine cut = new ByteMachine(); assertSame(nameState, cut.addPattern(Patterns.anythingButMatch("z"), nameState)); } @Test public void testAddAnythingButPatternNoNameStateGiven() { ByteMachine cut = new ByteMachine(); assertNotNull(cut.addPattern(Patterns.anythingButMatch("z"))); } @Test public void testAddRangePatternGivenNameStateReturned() { NameState nameState = new NameState(); ByteMachine cut = new ByteMachine(); assertSame(nameState, cut.addPattern(Range.lessThan(5), nameState)); } @Test public void testAddRangePatternNoNameStateGiven() { ByteMachine cut = new ByteMachine(); assertNotNull(cut.addPattern(Range.lessThan(5))); } private void testPatternPermutations(PatternMatch ... patternMatches) { testPatternPermutations(new String[0], patternMatches); } /** * Adds all given patterns to the machine according to all possible permutations. For a given permutation, as each * pattern is added, the machine is exercised against the given match values, and we verify that each match value * generates the expected number of matches. After adding the last pattern of a permutation and verifying the * matches, we delete all patterns, verifying the expected number of matches after each pattern deletion, and * verifying the machine is empty after the last pattern is deleted. Deletion can be done in one of two ways. For * sufficiently large numbers of permutations, we simply choose a random permutation for the deletion. However, for * smaller numbers of permutations, we perform deletion according to all permutations, i.e. for each addition * permutation, we exercise all deletion permutations, which is (n!)^2 where n is the number of patterns. * * Any match specified by one PatternMatch will be evaluated against all patterns. So if you specify that one * pattern has matched a certain value, you need to specify that same value for all other patterns that match it as * well. Provide values that will not match any patterns in the noMatches array and this function will verify that * they are never matched. If you provide a value in the noMatches array that is present in one of the PatternMatch * objects, it will have no effect; i.e. the value will still be expected to match the pattern. * * @param noMatches Array of values that do not match any of the given patterns. * @param patternMatches Array where each element contains a pattern and values that will match the pattern. */ private void testPatternPermutations(String[] noMatches, PatternMatch ... patternMatches) { long seed = new Random().nextLong(); System.out.println("USE ME TO REPRODUCE - ByteMachineTest.testPatternPermutations seeding with " + seed); Random r = new Random(seed); ByteMachine cut = new ByteMachine(); Set<String> matchValues = Stream.of(patternMatches) .map(patternMatch -> patternMatch.matches) .flatMap(set -> set.stream()) .collect(Collectors.toSet()); matchValues.addAll(Arrays.asList(noMatches)); Matches matches = new Matches(matchValues.stream() .map(string -> new Match(string)) .collect(Collectors.toList()) .toArray(new Match[0])); List<PatternMatch[]> permutations = generateAllPermutations(patternMatches); for (PatternMatch[] additionPermutation : permutations) { // Magic number alert: For 5 or less patterns, it is reasonable to test all deletion permutations for each // addition permutation. But for 6 or more patterns, the runtime becomes ridiculous, so we will settle for // choosing a random deletion permutation for each addition permutation. if (patternMatches.length <= 5) { for (PatternMatch[] deletionPermutation : permutations) { testPatternPermutation(cut, additionPermutation, deletionPermutation, matches); } } else { testPatternPermutation(cut, additionPermutation, permutations.get(r.nextInt(permutations.size())), matches); } } } private void testPatternPermutation(ByteMachine cut, PatternMatch[] additionPermutation, PatternMatch[] deletionPermutation, Matches matches) { for (PatternMatch patternMatch : additionPermutation) { cut.addPattern(matches.registerPattern(patternMatch)); for (Match match : matches.get()) { assertEquals("Failed on " + match.value, match.getNumPatternsRegistered(), cut.transitionOn(match.value).size()); } } for (PatternMatch patternMatch : deletionPermutation) { cut.deletePattern(matches.deregisterPattern(patternMatch)); for (Match match : matches.get()) { assertEquals("Failed on " + match.value, match.getNumPatternsRegistered(), cut.transitionOn(match.value).size()); } } assertTrue(cut.isEmpty()); matches.assertNoPatternsRegistered(); } private static class Matches { private final Match[] matches; public Matches(Match ... matches) { this.matches = matches; } public Match[] get() { return matches; } public Patterns registerPattern(PatternMatch patternMatch) { for (Match match : matches) { match.registerPattern(patternMatch); } return patternMatch.pattern; } public Patterns deregisterPattern(PatternMatch patternMatch) { for (Match match : matches) { match.deregisterPattern(patternMatch); } return patternMatch.pattern; } public void assertNoPatternsRegistered() { for (Match match : matches) { assertEquals(0, match.getNumPatternsRegistered()); } } } private static class Match { private final String value; private int num = 0; public Match(String value) { this.value = value; } public void registerPattern(PatternMatch patternMatch) { if (patternMatch.matches.contains(value)) { num++; } } public void deregisterPattern(PatternMatch patternMatch) { if (patternMatch.matches.contains(value)) { num--; } } public int getNumPatternsRegistered() { return num; } } private static class PatternMatch { private final Patterns pattern; private final Set<String> matches; public PatternMatch(Patterns pattern, String ... matches) { this.pattern = pattern; this.matches = new HashSet<>(Arrays.asList(matches)); } } }