1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.filter.codec;
21  
22  import java.net.SocketAddress;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import org.apache.mina.core.buffer.IoBuffer;
27  import org.apache.mina.core.service.DefaultTransportMetadata;
28  import org.apache.mina.core.session.IoSession;
29  import org.apache.mina.core.session.IoSessionConfig;
30  import org.junit.After;
31  import org.junit.Before;
32  import org.junit.Test;
33  import static org.junit.Assert.assertFalse;
34  import static org.junit.Assert.assertEquals;
35  import static org.junit.Assert.fail;
36  import static org.junit.Assert.assertTrue;
37  
38  
39  /**
40   * Tests {@link CumulativeProtocolDecoder}.
41   *
42   * @author The Apache MINA Project (dev@mina.apache.org)
43   * @version $Rev: 713100 $, $Date: 2008-11-11 19:39:46 +0100 (Tue, 11 Nov 2008) $
44   */
45  public class CumulativeProtocolDecoderTest {
46      private final ProtocolCodecSession session = new ProtocolCodecSession();
47  
48      private IoBuffer buf;
49      private IntegerDecoder decoder;
50  
51      @Before
52      public void setUp() throws Exception {
53          buf = IoBuffer.allocate(16);
54          decoder = new IntegerDecoder();
55          session.setTransportMetadata(
56                  new DefaultTransportMetadata(
57                          "mina", "dummy", false, true, SocketAddress.class,
58                          IoSessionConfig.class, IoBuffer.class));
59      }
60  
61      @After
62      public void tearDown() throws Exception {
63          decoder.dispose(session);
64      }
65  
66      @Test
67      public void testCumulation() throws Exception {
68          buf.put((byte) 0);
69          buf.flip();
70  
71          decoder.decode(session, buf, session.getDecoderOutput());
72          assertEquals(0, session.getDecoderOutputQueue().size());
73          assertEquals(buf.limit(), buf.position());
74  
75          buf.clear();
76          buf.put((byte) 0);
77          buf.put((byte) 0);
78          buf.put((byte) 1);
79          buf.flip();
80  
81          decoder.decode(session, buf, session.getDecoderOutput());
82          assertEquals(1, session.getDecoderOutputQueue().size());
83          assertEquals(new Integer(1), session.getDecoderOutputQueue().poll());
84          assertEquals(buf.limit(), buf.position());
85      }
86  
87      @Test
88      public void testRepeatitiveDecode() throws Exception {
89          for (int i = 0; i < 4; i++) {
90              buf.putInt(i);
91          }
92          buf.flip();
93  
94          decoder.decode(session, buf, session.getDecoderOutput());
95          assertEquals(4, session.getDecoderOutputQueue().size());
96          assertEquals(buf.limit(), buf.position());
97  
98          List<Object> expected = new ArrayList<Object>();
99          
100         for (int i = 0; i < 4; i++) {
101             expected.add(new Integer(i));
102         }
103         
104         assertEquals(expected, session.getDecoderOutputQueue());
105     }
106 
107     @Test
108     public void testWrongImplementationDetection() throws Exception {
109         try {
110             new WrongDecoder().decode(session, buf, session.getDecoderOutput());
111             fail();
112         } catch (IllegalStateException e) {
113             // OK
114         }
115     }
116     
117     @Test
118     public void testBufferDerivation() throws Exception {
119         decoder = new DuplicatingIntegerDecoder();
120         
121         buf.putInt(1);
122         
123         // Put some extra byte to make the decoder create an internal buffer.
124         buf.put((byte) 0);
125         buf.flip();
126 
127         decoder.decode(session, buf, session.getDecoderOutput());
128         assertEquals(1, session.getDecoderOutputQueue().size());
129         assertEquals(1, session.getDecoderOutputQueue().poll());
130         assertEquals(buf.limit(), buf.position());
131 
132         // Keep appending to the internal buffer.
133         // DuplicatingIntegerDecoder will keep duplicating the internal
134         // buffer to disable auto-expansion, and CumulativeProtocolDecoder
135         // should detect that user derived its internal buffer.
136         // Consequently, CumulativeProtocolDecoder will perform 
137         // reallocation to avoid putting incoming data into
138         // the internal buffer with auto-expansion disabled.
139         for (int i = 2; i < 10; i ++) {
140             buf.clear();
141             buf.putInt(i);
142             // Put some extra byte to make the decoder keep the internal buffer.
143             buf.put((byte) 0);
144             buf.flip();
145             buf.position(1);
146     
147             decoder.decode(session, buf, session.getDecoderOutput());
148             assertEquals(1, session.getDecoderOutputQueue().size());
149             assertEquals(i, session.getDecoderOutputQueue().poll());
150             assertEquals(buf.limit(), buf.position());
151         }
152     }
153 
154     private static class IntegerDecoder extends CumulativeProtocolDecoder {
155 
156         @Override
157         protected boolean doDecode(IoSession session, IoBuffer in,
158                 ProtocolDecoderOutput out) throws Exception {
159             assertTrue(in.hasRemaining());
160             
161             if (in.remaining() < 4) {
162                 return false;
163             }
164 
165             out.write(new Integer(in.getInt()));
166             return true;
167         }
168 
169         public void dispose() throws Exception {
170         }
171     }
172     
173     private static class WrongDecoder extends CumulativeProtocolDecoder {
174         @Override
175         protected boolean doDecode(IoSession session, IoBuffer in,
176                 ProtocolDecoderOutput out) throws Exception {
177             return true;
178         }
179 
180         public void dispose() throws Exception {
181         }
182     }
183 
184     private static class DuplicatingIntegerDecoder extends IntegerDecoder {
185         @Override
186         protected boolean doDecode(IoSession session, IoBuffer in,
187                 ProtocolDecoderOutput out) throws Exception {
188             in.duplicate(); // Will disable auto-expansion.
189             assertFalse(in.isAutoExpand());
190             return super.doDecode(session, in, out);
191         }
192 
193         public void dispose() throws Exception {
194         }
195     }
196 }