001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.filter.codec;
021
022import static org.junit.Assert.assertEquals;
023import static org.junit.Assert.assertFalse;
024import static org.junit.Assert.assertTrue;
025import static org.junit.Assert.fail;
026
027import java.net.SocketAddress;
028
029import org.apache.mina.core.buffer.IoBuffer;
030import org.apache.mina.core.service.DefaultTransportMetadata;
031import org.apache.mina.core.session.IoSession;
032import org.apache.mina.core.session.IoSessionConfig;
033import org.junit.After;
034import org.junit.Before;
035import org.junit.Test;
036
037/**
038 * Tests {@link CumulativeProtocolDecoder}.
039 *
040 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
041 */
042public class CumulativeProtocolDecoderTest {
043    private final ProtocolCodecSession session = new ProtocolCodecSession();
044
045    private IoBuffer buf;
046
047    private IntegerDecoder decoder;
048
049    @Before
050    public void setUp() throws Exception {
051        buf = IoBuffer.allocate(16);
052        decoder = new IntegerDecoder();
053        session.setTransportMetadata(new DefaultTransportMetadata("mina", "dummy", false, true, SocketAddress.class,
054                IoSessionConfig.class, IoBuffer.class));
055    }
056
057    @After
058    public void tearDown() throws Exception {
059        decoder.dispose(session);
060    }
061
062    @Test
063    public void testCumulation() throws Exception {
064        buf.put((byte) 0);
065        buf.flip();
066
067        decoder.decode(session, buf, session.getDecoderOutput());
068        assertEquals(0, session.getDecoderOutputQueue().size());
069        assertEquals(buf.limit(), buf.position());
070
071        buf.clear();
072        buf.put((byte) 0);
073        buf.put((byte) 0);
074        buf.put((byte) 1);
075        buf.flip();
076
077        decoder.decode(session, buf, session.getDecoderOutput());
078        assertEquals(1, session.getDecoderOutputQueue().size());
079        assertEquals(new Integer(1), session.getDecoderOutputQueue().poll());
080        assertEquals(buf.limit(), buf.position());
081    }
082
083    @Test
084    public void testRepeatitiveDecode() throws Exception {
085        for (int i = 0; i < 4; i++) {
086            buf.putInt(i);
087        }
088        buf.flip();
089
090        decoder.decode(session, buf, session.getDecoderOutput());
091        assertEquals(4, session.getDecoderOutputQueue().size());
092        assertEquals(buf.limit(), buf.position());
093
094        for (int i = 0; i < 4; i++) {
095            assertTrue(session.getDecoderOutputQueue().contains(i));
096        }
097    }
098
099    @Test
100    public void testWrongImplementationDetection() throws Exception {
101        try {
102            new WrongDecoder().decode(session, buf, session.getDecoderOutput());
103            fail();
104        } catch (IllegalStateException e) {
105            // OK
106        }
107    }
108
109    @Test
110    public void testBufferDerivation() throws Exception {
111        decoder = new DuplicatingIntegerDecoder();
112
113        buf.putInt(1);
114
115        // Put some extra byte to make the decoder create an internal buffer.
116        buf.put((byte) 0);
117        buf.flip();
118
119        decoder.decode(session, buf, session.getDecoderOutput());
120        assertEquals(1, session.getDecoderOutputQueue().size());
121        assertEquals(1, session.getDecoderOutputQueue().poll());
122        assertEquals(buf.limit(), buf.position());
123
124        // Keep appending to the internal buffer.
125        // DuplicatingIntegerDecoder will keep duplicating the internal
126        // buffer to disable auto-expansion, and CumulativeProtocolDecoder
127        // should detect that user derived its internal buffer.
128        // Consequently, CumulativeProtocolDecoder will perform 
129        // reallocation to avoid putting incoming data into
130        // the internal buffer with auto-expansion disabled.
131        for (int i = 2; i < 10; i++) {
132            buf.clear();
133            buf.putInt(i);
134            // Put some extra byte to make the decoder keep the internal buffer.
135            buf.put((byte) 0);
136            buf.flip();
137            buf.position(1);
138
139            decoder.decode(session, buf, session.getDecoderOutput());
140            assertEquals(1, session.getDecoderOutputQueue().size());
141            assertEquals(i, session.getDecoderOutputQueue().poll());
142            assertEquals(buf.limit(), buf.position());
143        }
144    }
145
146    private static class IntegerDecoder extends CumulativeProtocolDecoder {
147        /**
148         * Default constructor
149         */
150        public IntegerDecoder() {
151            super();
152        }
153
154        @Override
155        protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
156            assertTrue(in.hasRemaining());
157
158            if (in.remaining() < 4) {
159                return false;
160            }
161
162            out.write(new Integer(in.getInt()));
163            return true;
164        }
165    }
166
167    private static class WrongDecoder extends CumulativeProtocolDecoder {
168        /**
169         * Default constructor
170         */
171        public WrongDecoder() {
172            super();
173        }
174
175        @Override
176        protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
177            return true;
178        }
179    }
180
181    private static class DuplicatingIntegerDecoder extends IntegerDecoder {
182        /**
183         * Default constructor
184         */
185        public DuplicatingIntegerDecoder() {
186            super();
187        }
188
189        @Override
190        protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
191            in.duplicate(); // Will disable auto-expansion.
192            assertFalse(in.isAutoExpand());
193            return super.doDecode(session, in, out);
194        }
195    }
196}