From 0a75af931ec7a9c7038362ece4b2dd11d1118185 Mon Sep 17 00:00:00 2001 From: Anton Persson Date: Mon, 27 Aug 2018 12:58:29 +0200 Subject: [PATCH] Randomized test for UTF8StringValue compare Testing that StringValue and UTF8StringValue always return the same compare result. This does not actually work for strings that contain code points greater than 16 bits, as shown by disabled test in UTF8StringValueRandomTest. --- community/random-values/pom.xml | 6 ++ .../java/org/neo4j/test/rule/RandomRule.java | 10 +++ .../neo4j/values/storable/RandomValues.java | 52 ++++++----- .../storable/UTF8StringValueRandomTest.java | 89 +++++++++++++++++++ .../values/storable/UTF8StringValueTest.java | 25 ++++-- 5 files changed, 154 insertions(+), 28 deletions(-) create mode 100644 community/random-values/src/test/java/org/neo4j/values/storable/UTF8StringValueRandomTest.java diff --git a/community/random-values/pom.xml b/community/random-values/pom.xml index 632387f5af0ea..89a99e115c282 100644 --- a/community/random-values/pom.xml +++ b/community/random-values/pom.xml @@ -75,6 +75,12 @@ ${project.version} test-jar + + org.neo4j + neo4j-values + ${project.version} + test-jar + org.junit.jupiter junit-jupiter-migrationsupport diff --git a/community/random-values/src/main/java/org/neo4j/test/rule/RandomRule.java b/community/random-values/src/main/java/org/neo4j/test/rule/RandomRule.java index 34a26ad180f21..2967937880905 100644 --- a/community/random-values/src/main/java/org/neo4j/test/rule/RandomRule.java +++ b/community/random-values/src/main/java/org/neo4j/test/rule/RandomRule.java @@ -199,6 +199,16 @@ public String nextAlphaNumericString( ) return nextAlphaNumericTextValue().stringValue(); } + public String nextAsciiString() + { + return nextAsciiTextValue().stringValue(); + } + + private TextValue nextAsciiTextValue() + { + return randoms.nextAsciiTextValue(); + } + public TextValue nextAlphaNumericTextValue( ) { return randoms.nextAlphaNumericTextValue(); diff --git a/community/random-values/src/main/java/org/neo4j/values/storable/RandomValues.java b/community/random-values/src/main/java/org/neo4j/values/storable/RandomValues.java index a702ffc495ef9..654b228d4c72a 100644 --- a/community/random-values/src/main/java/org/neo4j/values/storable/RandomValues.java +++ b/community/random-values/src/main/java/org/neo4j/values/storable/RandomValues.java @@ -93,6 +93,8 @@ public interface Configuration int arrayMinLength(); int arrayMaxLength(); + + int maxCodePoint(); } public static class Default implements Configuration @@ -120,8 +122,15 @@ public int arrayMaxLength() { return 10; } + + @Override + public int maxCodePoint() + { + return Character.MAX_CODE_POINT; + } } + public static final int MAX_16_BIT_CODE_POINT = Character.MIN_SUPPLEMENTARY_CODE_POINT - 1; public static Configuration DEFAULT_CONFIGURATION = new Default(); private static Types[] TYPES = Types.values(); private static final long NANOS_PER_SECOND = 1_000_000_000L; @@ -619,29 +628,32 @@ public TextValue nextTextValue( int minLength, int maxLength ) for ( int i = 0; i < length; i++ ) { - boolean validCodePoint = false; - - //TODO it is a bit inefficient to generate integer and then retry if we end up in an invalid range - //instead we could always generate values in a valid range, however there are a lot of ranges with holes - //so the code will probably become a bit unwieldly - while ( !validCodePoint ) - { - int codePoint = intBetween( Character.MIN_CODE_POINT, Character.MAX_CODE_POINT ); - switch ( Character.getType( codePoint ) ) - { - case Character.UNASSIGNED: - case Character.PRIVATE_USE: - case Character.SURROGATE: - continue; - default: - builder.addCodePoint( codePoint ); - validCodePoint = true; - } - } + builder.addCodePoint( nextValidCodePoint() ); } return builder.build(); } + /** + * Generate next code point that is valid for composition of a string. + * Additional limitation on code point range is given by configuration. + * + * @return A pseudorandom valid code point + */ + private int nextValidCodePoint() + { + int codePoint; + int type; + do + { + codePoint = intBetween( Character.MIN_CODE_POINT, configuration.maxCodePoint() ); + type = Character.getType( codePoint ); + } + while ( type == Character.UNASSIGNED || + type == Character.PRIVATE_USE || + type == Character.SURROGATE ); + return codePoint; + } + /** * Returns the next pseudorandom {@link Value}, distributed uniformly among the supported Value types. *

@@ -1496,7 +1508,7 @@ public TextArray nextStringArray() * @param maxLength the maximum length of the array * @return the next pseudorandom {@link TextArray}. */ - public TextArray nextStringArray( int minLength, int maxLength ) + private TextArray nextStringArray( int minLength, int maxLength ) { int length = intBetween( minLength, maxLength ); String[] strings = new String[length]; diff --git a/community/random-values/src/test/java/org/neo4j/values/storable/UTF8StringValueRandomTest.java b/community/random-values/src/test/java/org/neo4j/values/storable/UTF8StringValueRandomTest.java new file mode 100644 index 0000000000000..c843f8bccd4b1 --- /dev/null +++ b/community/random-values/src/test/java/org/neo4j/values/storable/UTF8StringValueRandomTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.values.storable; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.neo4j.test.extension.Inject; +import org.neo4j.test.extension.RandomExtension; +import org.neo4j.test.rule.RandomRule; + +@ExtendWith( RandomExtension.class ) +class UTF8StringValueRandomTest +{ + @Inject + RandomRule random; + + @Test + void shouldCompareToRandomAlphanumericString() + { + for ( int i = 0; i < 100; i++ ) + { + String string1 = random.nextAlphaNumericString(); + String string2 = random.nextAlphaNumericString(); + UTF8StringValueTest.assertCompareTo( string1, string2 ); + } + } + + @Test + void shouldCompareToAsciiString() + { + for ( int i = 0; i < 100; i++ ) + { + String string1 = random.nextAsciiString(); + String string2 = random.nextAsciiString(); + UTF8StringValueTest.assertCompareTo( string1, string2 ); + } + } + + @Test + void shouldCompare16BitCodePointString() + { + random.withConfiguration( new RandomValues.Default() + { + @Override + public int maxCodePoint() + { + return RandomValues.MAX_16_BIT_CODE_POINT; + } + } ); + random.reset(); + for ( int i = 0; i < 100; i++ ) + { + String string1 = random.nextString(); + String string2 = random.nextString(); + UTF8StringValueTest.assertCompareTo( string1, string2 ); + } + } + + @Disabled( "Comparing strings with higher than 16 bits code points is known to be inconsistent between StringValue and UTF8StringValue" ) + @Test + void shouldCompareToRandomString() + { + for ( int i = 0; i < 100; i++ ) + { + String string1 = random.nextString(); + String string2 = random.nextString(); + UTF8StringValueTest.assertCompareTo( string1, string2 ); + } + } +} diff --git a/community/values/src/test/java/org/neo4j/values/storable/UTF8StringValueTest.java b/community/values/src/test/java/org/neo4j/values/storable/UTF8StringValueTest.java index 2fb44a81a96a7..d3ab258bac1d2 100644 --- a/community/values/src/test/java/org/neo4j/values/storable/UTF8StringValueTest.java +++ b/community/values/src/test/java/org/neo4j/values/storable/UTF8StringValueTest.java @@ -107,18 +107,27 @@ void shouldCompareTo() { for ( String string2 : STRINGS ) { - - int x = stringValue( string1 ).compareTo( utf8Value( string2.getBytes( UTF_8 ) ) ); - int y = utf8Value( string1.getBytes( UTF_8 ) ).compareTo( stringValue( string2 ) ); - int z = utf8Value( string1.getBytes( UTF_8 ) ) - .compareTo( utf8Value( string2.getBytes( UTF_8 ) ) ); - - assertThat( Math.signum( x ), equalTo( Math.signum( y ) ) ); - assertThat( Math.signum( x ), equalTo( Math.signum( z ) ) ); + assertCompareTo( string1, string2 ); } } } + public static void assertCompareTo( String string1, String string2 ) + { + TextValue textValue1 = stringValue( string1 ); + TextValue textValue2 = stringValue( string2 ); + TextValue utf8Value1 = utf8Value( string1.getBytes( UTF_8 ) ); + TextValue utf8Value2 = utf8Value( string2.getBytes( UTF_8 ) ); + int a = textValue1.compareTo( textValue2 ); + int x = textValue1.compareTo( utf8Value2 ); + int y = utf8Value1.compareTo( textValue2 ); + int z = utf8Value1.compareTo( utf8Value2 ); + + assertThat( Math.signum( a ), equalTo( Math.signum( x ) ) ); + assertThat( Math.signum( a ), equalTo( Math.signum( y ) ) ); + assertThat( Math.signum( a ), equalTo( Math.signum( z ) ) ); + } + @Test void shouldReverse() {