ionutzpi commented on code in PR #1526: URL: https://github.com/apache/jackrabbit-oak/pull/1526#discussion_r1689259248
########## oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateTest.java: ########## @@ -81,4 +114,289 @@ public void multiValuedBinarySize() throws Exception { assertEquals(0, reads.size()); } -} + @Test + public void multiValuedAboveThresholdSize() throws Exception { + NodeBuilder builder = ns.getRoot().builder(); + List<Blob> blobs = newArrayList(); + for (int i = 0; i < 13; i++) { + blobs.add(builder.createBlob(new RandomStream(BLOB_SIZE, i))); + } + builder.child(TEST_NODE).setProperty("p", blobs, Type.BINARIES); + TestUtils.merge(ns, builder); + + PropertyState p = ns.getRoot().getChildNode(TEST_NODE).getProperty("p"); + assertEquals(Type.BINARIES, Objects.requireNonNull(p).getType()); + assertEquals(13, p.count()); + + reads.clear(); + assertEquals(BLOB_SIZE, p.size(0)); + // must not read the blob via stream + assertEquals(0, reads.size()); + } + + @Test + public void stringBelowThresholdSize() throws Exception { + NodeBuilder builder = ns.getRoot().builder(); + builder.child(TEST_NODE).setProperty("p", "dummy", Type.STRING); + TestUtils.merge(ns, builder); + + PropertyState p = ns.getRoot().getChildNode(TEST_NODE).getProperty("p"); + assertEquals(Type.STRING, Objects.requireNonNull(p).getType()); + assertEquals(1, p.count()); + + reads.clear(); + assertEquals(5, p.size(0)); + // must not read the string via stream + assertEquals(0, reads.size()); + } + + @Test + public void stringAboveThresholdSize() throws Exception { + NodeBuilder builder = ns.getRoot().builder(); + builder.child(TEST_NODE).setProperty("p", STRING_HUGEVALUE, Type.STRING); + TestUtils.merge(ns, builder); + + PropertyState p = ns.getRoot().getChildNode(TEST_NODE).getProperty("p"); + assertEquals(Type.STRING, Objects.requireNonNull(p).getType()); + assertEquals(1, p.count()); + + reads.clear(); + assertEquals(10050, p.size(0)); + // must not read the string via streams + assertEquals(0, reads.size()); + } + + @Test + public void compressValueThrowsException() throws IOException, NoSuchFieldException, IllegalAccessException { + DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); + Compression mockCompression = mock(Compression.class); + when(mockCompression.getOutputStream(any(OutputStream.class))).thenThrow(new IOException("Compression failed")); + + DocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); + DocumentPropertyState documentPropertyState = new DocumentPropertyState(mockDocumentStore, "p", "\"" + STRING_HUGEVALUE + "\"", mockCompression); + + assertEquals(documentPropertyState.getValue(Type.STRING), STRING_HUGEVALUE); + + verify(mockCompression, times(1)).getOutputStream(any(OutputStream.class)); + } + + @Test + public void uncompressValueThrowsException() throws IOException, NoSuchFieldException, IllegalAccessException { + + DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); + Compression mockCompression = mock(Compression.class); + OutputStream mockOutputStream= mock(OutputStream.class); + when(mockCompression.getOutputStream(any(OutputStream.class))).thenReturn(mockOutputStream); + when(mockCompression.getInputStream(any(InputStream.class))).thenThrow(new IOException("Compression failed")); + + DocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); + DocumentPropertyState documentPropertyState = new DocumentPropertyState(mockDocumentStore, "p", STRING_HUGEVALUE, mockCompression); + + assertEquals(documentPropertyState.getValue(Type.STRING), "{}"); + + verify(mockCompression, times(1)).getInputStream(any(InputStream.class)); + } + + @Test + public void defaultValueSetToMinusOne() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { + DocumentNodeStore store = mock(DocumentNodeStore.class); + + DocumentPropertyState.setCompressionThreshold(-1); + DocumentPropertyState state = new DocumentPropertyState(store, "propertyNameNew", "\"" + STRING_HUGEVALUE + "\"", Compression.GZIP); + + byte[] result = state.getCompressedValue(); + + assertNull(result); + assertEquals(state.getValue(Type.STRING), STRING_HUGEVALUE); + } + + @Test + public void stringAboveThresholdSizeNoCompression() { + DocumentNodeStore store = mock(DocumentNodeStore.class); + + DocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); + + DocumentPropertyState state = new DocumentPropertyState(store, "propertyName", "\"" + STRING_HUGEVALUE + "\"", Compression.NONE); + + byte[] result = state.getCompressedValue(); + + assertEquals(result.length, STRING_HUGEVALUE.length() + 2 ); + + assertEquals(state.getValue(Type.STRING), STRING_HUGEVALUE); + assertEquals(STRING_HUGEVALUE, state.getValue(Type.STRING)); + } + + @Test + public void testInterestingStringsWithoutCompression() { + DocumentNodeStore store = mock(DocumentNodeStore.class); + String[] tests = new String[] { "simple:foo", "cr:a\n\b", "dquote:a\"b", "bs:a\\b", "euro:a\u201c", "gclef:\uD834\uDD1E", + "tab:a\tb", "nul:a\u0000b"}; + + for (String test : tests) { + DocumentPropertyState state = new DocumentPropertyState(store, "propertyName", test, Compression.GZIP); + assertEquals(test, state.getValue()); + } + } + + @Test + public void testInterestingStringsWithCompression() { + DocumentNodeStore store = mock(DocumentNodeStore.class); + String[] tests = new String[]{"simple:foo", "cr:a\n\b", "dquote:a\"b", "bs:a\\b", "euro:a\u201c", "gclef:\uD834\uDD1E", + "tab:a\tb", "nul:a\u0000b"}; + + DocumentPropertyState.setCompressionThreshold(1); + for (String test : tests) { + DocumentPropertyState state = new DocumentPropertyState(store, "propertyName", test, Compression.GZIP); + assertEquals(test, state.getValue()); + } + } + + @Test + public void testBrokenSurrogateWithoutCompressionForMongo() throws CommitFailedException { + getBrokenSurrogateAndInitializeDifferentStores(MONGO, false); + } + + @Test + public void testBrokenSurrogateWithoutCompressionForRDB() throws CommitFailedException { + getBrokenSurrogateAndInitializeDifferentStores(RDB_H2, false); + } + + @Test + public void testBrokenSurrogateWithoutCompressionForInMemory() throws CommitFailedException { + getBrokenSurrogateAndInitializeDifferentStores(MEMORY, false); + } + + @Test + public void testBrokenSurrogateWithCompressionForMongo() throws CommitFailedException { + DocumentPropertyState.setCompressionThreshold(1); + getBrokenSurrogateAndInitializeDifferentStores(MONGO, true); + } + + @Test + public void testBrokenSurrogateWithCompressionForRDB() throws CommitFailedException { + DocumentPropertyState.setCompressionThreshold(1); + getBrokenSurrogateAndInitializeDifferentStores(RDB_H2, true); + } + + @Test + public void testBrokenSurrogateWithCompressionForInMemory() throws CommitFailedException { + DocumentPropertyState.setCompressionThreshold(1); + getBrokenSurrogateAndInitializeDifferentStores(MEMORY, true); + } + + private void getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture fixture, boolean compressionEnabled) throws CommitFailedException { + String test = "brokensurrogate:dfsa\ud800"; + DocumentStore store = null; + DocumentNodeStore nodeStore = null; + + try { + store = fixture.createDocumentStore(); + + if (store instanceof MongoDocumentStore) { + // Enforce primary read preference, otherwise tests may fail on a + // replica set with a read preference configured to secondary. + // Revision GC usually runs with a modified range way in the past, + // which means changes made it to the secondary, but not in this + // test using a virtual clock + MongoTestUtils.setReadPreference(store, ReadPreference.primary()); + } + nodeStore = new DocumentMK.Builder().setDocumentStore(store).getNodeStore(); + + createPropAndCheckValue(nodeStore, test, compressionEnabled); + + } finally { + if (nodeStore != null) { + nodeStore.dispose(); + } + if (store != null) { + store.dispose(); + } + } + + } + + private void createPropAndCheckValue(DocumentNodeStore nodeStore, String test, boolean compressionEnabled) throws CommitFailedException { + NodeBuilder builder = nodeStore.getRoot().builder(); + if (compressionEnabled) { + DocumentPropertyState.setCompressionThreshold(1); + } + DocumentPropertyState documentPropertyState = new DocumentPropertyState(nodeStore, "p", test, Compression.GZIP); + builder.child(TEST_NODE).setProperty("p", documentPropertyState.getValue(), Type.STRING); Review Comment: Done. Thanks. ########## oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateTest.java: ########## @@ -81,4 +114,289 @@ public void multiValuedBinarySize() throws Exception { assertEquals(0, reads.size()); } -} + @Test + public void multiValuedAboveThresholdSize() throws Exception { + NodeBuilder builder = ns.getRoot().builder(); + List<Blob> blobs = newArrayList(); + for (int i = 0; i < 13; i++) { + blobs.add(builder.createBlob(new RandomStream(BLOB_SIZE, i))); + } + builder.child(TEST_NODE).setProperty("p", blobs, Type.BINARIES); + TestUtils.merge(ns, builder); + + PropertyState p = ns.getRoot().getChildNode(TEST_NODE).getProperty("p"); + assertEquals(Type.BINARIES, Objects.requireNonNull(p).getType()); + assertEquals(13, p.count()); + + reads.clear(); + assertEquals(BLOB_SIZE, p.size(0)); + // must not read the blob via stream + assertEquals(0, reads.size()); + } + + @Test + public void stringBelowThresholdSize() throws Exception { + NodeBuilder builder = ns.getRoot().builder(); + builder.child(TEST_NODE).setProperty("p", "dummy", Type.STRING); + TestUtils.merge(ns, builder); + + PropertyState p = ns.getRoot().getChildNode(TEST_NODE).getProperty("p"); + assertEquals(Type.STRING, Objects.requireNonNull(p).getType()); + assertEquals(1, p.count()); + + reads.clear(); + assertEquals(5, p.size(0)); + // must not read the string via stream + assertEquals(0, reads.size()); + } + + @Test + public void stringAboveThresholdSize() throws Exception { + NodeBuilder builder = ns.getRoot().builder(); + builder.child(TEST_NODE).setProperty("p", STRING_HUGEVALUE, Type.STRING); + TestUtils.merge(ns, builder); + + PropertyState p = ns.getRoot().getChildNode(TEST_NODE).getProperty("p"); + assertEquals(Type.STRING, Objects.requireNonNull(p).getType()); + assertEquals(1, p.count()); + + reads.clear(); + assertEquals(10050, p.size(0)); + // must not read the string via streams + assertEquals(0, reads.size()); + } + + @Test + public void compressValueThrowsException() throws IOException, NoSuchFieldException, IllegalAccessException { + DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); + Compression mockCompression = mock(Compression.class); + when(mockCompression.getOutputStream(any(OutputStream.class))).thenThrow(new IOException("Compression failed")); + + DocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); + DocumentPropertyState documentPropertyState = new DocumentPropertyState(mockDocumentStore, "p", "\"" + STRING_HUGEVALUE + "\"", mockCompression); + + assertEquals(documentPropertyState.getValue(Type.STRING), STRING_HUGEVALUE); + + verify(mockCompression, times(1)).getOutputStream(any(OutputStream.class)); + } + + @Test + public void uncompressValueThrowsException() throws IOException, NoSuchFieldException, IllegalAccessException { + + DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); + Compression mockCompression = mock(Compression.class); + OutputStream mockOutputStream= mock(OutputStream.class); + when(mockCompression.getOutputStream(any(OutputStream.class))).thenReturn(mockOutputStream); + when(mockCompression.getInputStream(any(InputStream.class))).thenThrow(new IOException("Compression failed")); + + DocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); + DocumentPropertyState documentPropertyState = new DocumentPropertyState(mockDocumentStore, "p", STRING_HUGEVALUE, mockCompression); + + assertEquals(documentPropertyState.getValue(Type.STRING), "{}"); + + verify(mockCompression, times(1)).getInputStream(any(InputStream.class)); + } + + @Test + public void defaultValueSetToMinusOne() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { + DocumentNodeStore store = mock(DocumentNodeStore.class); + + DocumentPropertyState.setCompressionThreshold(-1); + DocumentPropertyState state = new DocumentPropertyState(store, "propertyNameNew", "\"" + STRING_HUGEVALUE + "\"", Compression.GZIP); + + byte[] result = state.getCompressedValue(); + + assertNull(result); + assertEquals(state.getValue(Type.STRING), STRING_HUGEVALUE); + } + + @Test + public void stringAboveThresholdSizeNoCompression() { + DocumentNodeStore store = mock(DocumentNodeStore.class); + + DocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); + + DocumentPropertyState state = new DocumentPropertyState(store, "propertyName", "\"" + STRING_HUGEVALUE + "\"", Compression.NONE); + + byte[] result = state.getCompressedValue(); + + assertEquals(result.length, STRING_HUGEVALUE.length() + 2 ); + + assertEquals(state.getValue(Type.STRING), STRING_HUGEVALUE); + assertEquals(STRING_HUGEVALUE, state.getValue(Type.STRING)); + } + + @Test + public void testInterestingStringsWithoutCompression() { + DocumentNodeStore store = mock(DocumentNodeStore.class); + String[] tests = new String[] { "simple:foo", "cr:a\n\b", "dquote:a\"b", "bs:a\\b", "euro:a\u201c", "gclef:\uD834\uDD1E", + "tab:a\tb", "nul:a\u0000b"}; + + for (String test : tests) { + DocumentPropertyState state = new DocumentPropertyState(store, "propertyName", test, Compression.GZIP); + assertEquals(test, state.getValue()); Review Comment: Done ########## oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateTest.java: ########## @@ -18,26 +18,50 @@ import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; import java.util.List; +import java.util.Map; Review Comment: Done -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: oak-dev-unsubscr...@jackrabbit.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org