diff --git a/mina-filter-compression/pom.xml b/mina-filter-compression/pom.xml index 097002daf..a2cc76ff1 100644 --- a/mina-filter-compression/pom.xml +++ b/mina-filter-compression/pom.xml @@ -45,8 +45,8 @@ - org.easymock - easymock + org.mockito + mockito-core diff --git a/mina-filter-compression/src/main/java/org/apache/mina/filter/compression/CompressionFilter.java b/mina-filter-compression/src/main/java/org/apache/mina/filter/compression/CompressionFilter.java index cfc2063aa..abecdf05d 100644 --- a/mina-filter-compression/src/main/java/org/apache/mina/filter/compression/CompressionFilter.java +++ b/mina-filter-compression/src/main/java/org/apache/mina/filter/compression/CompressionFilter.java @@ -277,9 +277,9 @@ public void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) t throw new IllegalStateException("Only one " + CompressionFilter.class + " is permitted."); } - Zlib deflater = new Zlib(compressionLevel, Zlib.MODE_INFLATER, maxDecompressedSize, + Zlib deflater = new Zlib(compressionLevel, Zlib.MODE_DEFLATER, maxDecompressedSize, maxDecompressRatio, decompressRatioMinSize); - Zlib inflater = new Zlib(compressionLevel, Zlib.MODE_INFLATER, maxDecompressedSize, + Zlib inflater = new Zlib(compressionLevel, Zlib.MODE_INFLATER, maxDecompressedSize, maxDecompressRatio, decompressRatioMinSize); IoSession session = parent.getSession(); diff --git a/mina-filter-compression/src/test/java/org/apache/mina/filter/compression/CompressionFilterTest.java b/mina-filter-compression/src/test/java/org/apache/mina/filter/compression/CompressionFilterTest.java index 9a55006dc..cadd544de 100644 --- a/mina-filter-compression/src/test/java/org/apache/mina/filter/compression/CompressionFilterTest.java +++ b/mina-filter-compression/src/test/java/org/apache/mina/filter/compression/CompressionFilterTest.java @@ -19,192 +19,112 @@ */ package org.apache.mina.filter.compression; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; import org.apache.mina.core.buffer.IoBuffer; -import org.apache.mina.core.filterchain.IoFilterChain; import org.apache.mina.core.filterchain.IoFilter.NextFilter; +import org.apache.mina.core.filterchain.IoFilterChain; +import org.apache.mina.core.session.AttributeKey; import org.apache.mina.core.session.IoSession; import org.apache.mina.core.write.DefaultWriteRequest; import org.apache.mina.core.write.WriteRequest; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; +import org.mockito.ArgumentCaptor; /** - * + * * @author Apache MINA Project */ -@Ignore public class CompressionFilterTest { - /* - private MockControl mockSession; - - private MockControl mockNextFilter; - - private MockControl mockIoFilterChain; - - private IoSession session; - - private NextFilter nextFilter; - - private IoFilterChain ioFilterChain; + // the sample data to be used for testing + private static final String STR_COMPRESS = repeat("The quick brown fox jumps over the lazy dog. ", 25); private CompressionFilter filter; - private Zlib deflater; - - private Zlib inflater; + private IoSession session; - private Zlib actualDeflater; + private IoFilterChain filterChain; - private Zlib actualInflater; + private NextFilter nextFilter; - // the sample data to be used for testing - String strCompress = "The quick brown fox jumps over the lazy dog. " - + "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. " - + "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. " - + "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. " - + "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. " - + "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. " - + "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. " - + "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. " - + "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. " - + "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. " - + "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. " - + "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. " - + "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. "; + private static String repeat(String value, int count) { + StringBuilder builder = new StringBuilder(value.length() * count); + for (int i = 0; i < count; i++) { + builder.append(value); + } + return builder.toString(); + } @Before public void setUp() { - // create the necessary mock controls. - mockSession = MockControl.createControl(IoSession.class); - mockNextFilter = MockControl.createControl(NextFilter.class); - mockIoFilterChain = MockControl.createControl(IoFilterChain.class); - - // set the default matcher - mockNextFilter.setDefaultMatcher(new DataMatcher()); - - session = (IoSession) mockSession.getMock(); - nextFilter = (NextFilter) mockNextFilter.getMock(); - ioFilterChain = (IoFilterChain) mockIoFilterChain.getMock(); - - // create an instance of the filter filter = new CompressionFilter(CompressionFilter.COMPRESSION_MAX); - // deflater and inflater that will be used by the filter - deflater = new Zlib(Zlib.COMPRESSION_MAX, Zlib.MODE_DEFLATER); - inflater = new Zlib(Zlib.COMPRESSION_MAX, Zlib.MODE_INFLATER); - - // create instances of the deflater and inflater to help test the output - actualDeflater = new Zlib(Zlib.COMPRESSION_MAX, Zlib.MODE_DEFLATER); - actualInflater = new Zlib(Zlib.COMPRESSION_MAX, Zlib.MODE_INFLATER); + // a mock session whose attributes are stored in a real map, so that the deflater and inflater + // created by onPreAdd() are actually retrieved by filterWrite() and messageReceived(). + session = mock(IoSession.class); + final Map attributes = new HashMap<>(); + when(session.setAttribute(any(), any())) + .thenAnswer(invocation -> attributes.put(invocation.getArgument(0), invocation.getArgument(1))); + when(session.getAttribute(any())).thenAnswer(invocation -> attributes.get(invocation.getArgument(0))); + when(session.containsAttribute(any())).thenAnswer(invocation -> attributes.containsKey(invocation.getArgument(0))); + when(session.removeAttribute(any())).thenAnswer(invocation -> attributes.remove(invocation.getArgument(0))); + + filterChain = mock(IoFilterChain.class); + when(filterChain.contains(CompressionFilter.class)).thenReturn(false); + when(filterChain.getSession()).thenReturn(session); + + nextFilter = mock(NextFilter.class); } @Test - public void testCompression() throws Exception { - // prepare the input data - IoBuffer buf = IoBuffer.wrap(strCompress.getBytes(StandardCharsets.UTF_8)); - IoBuffer actualOutput = actualDeflater.deflate(buf); - buf.flip(); - WriteRequest writeRequest = new DefaultWriteRequest(buf); + public void testCompressionRoundTrip() throws Exception { + filter.onPreAdd(filterChain, "CompressionFilter", nextFilter); - // record all the mock calls - ioFilterChain.contains(CompressionFilter.class); - mockIoFilterChain.setReturnValue(false); - - ioFilterChain.getSession(); - mockIoFilterChain.setReturnValue(session); - - session.setAttribute(CompressionFilter.class.getName() + ".Deflater", deflater); - mockSession.setDefaultMatcher(new DataMatcher()); - mockSession.setReturnValue(null, MockControl.ONE); - - session.setAttribute(CompressionFilter.class.getName() + ".Inflater", inflater); - mockSession.setReturnValue(null, MockControl.ONE); - - session.containsAttribute(CompressionFilter.DISABLE_COMPRESSION_ONCE); - mockSession.setReturnValue(false); - - session.getAttribute(CompressionFilter.class.getName() + ".Deflater"); - mockSession.setReturnValue(deflater); - - nextFilter.filterWrite(session, new DefaultWriteRequest(actualOutput)); - - // switch to playback mode - mockSession.replay(); - mockIoFilterChain.replay(); - mockNextFilter.replay(); - - // make the actual calls on the filter - filter.onPreAdd(ioFilterChain, "CompressionFilter", nextFilter); + IoBuffer input = IoBuffer.wrap(STR_COMPRESS.getBytes(StandardCharsets.UTF_8)); + WriteRequest writeRequest = new DefaultWriteRequest(input); filter.filterWrite(nextFilter, session, writeRequest); - // verify that all the calls happened as recorded - mockNextFilter.verify(); + // capture the compressed buffer forwarded down the chain + ArgumentCaptor writeCaptor = ArgumentCaptor.forClass(WriteRequest.class); + verify(nextFilter).filterWrite(eq(session), writeCaptor.capture()); + IoBuffer compressed = (IoBuffer) writeCaptor.getValue().getMessage(); - assertTrue(true); + // feeding the compressed buffer back in must reproduce the original payload + filter.messageReceived(nextFilter, session, compressed); + ArgumentCaptor receiveCaptor = ArgumentCaptor.forClass(Object.class); + verify(nextFilter).messageReceived(eq(session), receiveCaptor.capture()); + IoBuffer decompressed = (IoBuffer) receiveCaptor.getValue(); + + assertEquals(STR_COMPRESS, decompressed.getString(StandardCharsets.UTF_8.newDecoder())); } + /** + * Regression guard: onPreAdd() must register the deflater in deflate mode and the inflater in + * inflate mode, not the other way round. Verified by checking each rejects the opposite operation. + */ @Test - public void testDecompression() throws Exception { - // prepare the input data - IoBuffer buf = IoBuffer.wrap(strCompress.getBytes(StandardCharsets.UTF_8)); - IoBuffer byteInput = actualDeflater.deflate(buf); - IoBuffer actualOutput = actualInflater.inflate(byteInput); + public void testDeflaterAndInflaterNotSwapped() throws Exception { + filter.onPreAdd(filterChain, "CompressionFilter", nextFilter); - // record all the mock calls - ioFilterChain.contains(CompressionFilter.class); - mockIoFilterChain.setReturnValue(false); + IoBuffer input = IoBuffer.wrap(STR_COMPRESS.getBytes(StandardCharsets.UTF_8)); - ioFilterChain.getSession(); - mockIoFilterChain.setReturnValue(session); + Zlib deflater = (Zlib) session.getAttribute(new AttributeKey(CompressionFilter.class, "deflater")); + assertNotNull(deflater); + assertThrows(IllegalStateException.class, () -> deflater.inflate(input)); - session.setAttribute(CompressionFilter.class.getName() + ".Deflater", deflater); - mockSession.setDefaultMatcher(new DataMatcher()); - mockSession.setReturnValue(null, MockControl.ONE); - session.setAttribute(CompressionFilter.class.getName() + ".Inflater", inflater); - mockSession.setReturnValue(null, MockControl.ONE); - - session.getAttribute(CompressionFilter.class.getName() + ".Inflater"); - mockSession.setReturnValue(inflater); - - nextFilter.messageReceived(session, actualOutput); - - // switch to playback mode - mockSession.replay(); - mockIoFilterChain.replay(); - mockNextFilter.replay(); - - // make the actual calls on the filter - filter.onPreAdd(ioFilterChain, "CompressionFilter", nextFilter); - filter.messageReceived(nextFilter, session, byteInput); - - // verify that all the calls happened as recorded - mockNextFilter.verify(); - - assertTrue(true); - } - - /** - * A matcher used to check if the actual and expected outputs matched - * - class DataMatcher extends AbstractMatcher { - @Override - protected boolean argumentMatches(Object arg0, Object arg1) { - // we need to only verify the ByteBuffer output - if (arg0 instanceof WriteRequest) { - WriteRequest expected = (WriteRequest) arg0; - WriteRequest actual = (WriteRequest) arg1; - IoBuffer bExpected = (IoBuffer) expected.getMessage(); - IoBuffer bActual = (IoBuffer) actual.getMessage(); - return bExpected.equals(bActual); - } - return true; - } + Zlib inflater = (Zlib) session.getAttribute(new AttributeKey(CompressionFilter.class, "inflater")); + assertNotNull(inflater); + assertThrows(IllegalStateException.class, () -> inflater.deflate(input)); } - */ }