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.util.byteaccess;
21  
22  import static org.easymock.EasyMock.createStrictControl;
23  
24  import java.nio.ByteOrder;
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  import junit.framework.TestCase;
29  
30  import org.apache.mina.core.buffer.IoBuffer;
31  import org.apache.mina.util.byteaccess.ByteArray.Cursor;
32  import org.apache.mina.util.byteaccess.CompositeByteArray.CursorListener;
33  import org.apache.mina.util.byteaccess.CompositeByteArrayRelativeWriter.ChunkedExpander;
34  import org.apache.mina.util.byteaccess.CompositeByteArrayRelativeWriter.Flusher;
35  import org.easymock.IMocksControl;
36  
37  /**
38   * Tests classes in the <code>byteaccess</code> package.
39   * 
40   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
41   */
42  public class ByteAccessTest extends TestCase {
43  
44      private List<String> operations = new ArrayList<String>();
45  
46      private void resetOperations() {
47          operations.clear();
48      }
49  
50      private void assertOperationCountEquals(int expectedCount) {
51          assertEquals("Operations: " + operations, expectedCount, operations.size());
52      }
53  
54      private void addOperation(String description) {
55          operations.add(description);
56      }
57  
58      public void testBufferByteArray() throws Exception {
59          ByteArray ba = getByteArrayFactory().create(1000);
60          testAbsoluteReaderAndWriter(0, 1000, ba, ba);
61          testAbsoluteReaderAndWriter(0, 1000, ba, ba);
62          Cursor readCursor = ba.cursor();
63          Cursor writeCursor = ba.cursor();
64          testRelativeReaderAndWriter(1000, readCursor, writeCursor);
65      }
66  
67      public void testCompositeAddAndRemove() throws Exception {
68          CompositeByteArray cba = new CompositeByteArray();
69          assertEquals(0, cba.first());
70          assertEquals(0, cba.last());
71          cba.addFirst(getByteArrayFactory().create(100));
72          assertEquals(-100, cba.first());
73          assertEquals(0, cba.last());
74          cba.addFirst(getByteArrayFactory().create(100));
75          assertEquals(-200, cba.first());
76          assertEquals(0, cba.last());
77          cba.addLast(getByteArrayFactory().create(100));
78          assertEquals(-200, cba.first());
79          assertEquals(100, cba.last());
80          cba.removeFirst();
81          assertEquals(-100, cba.first());
82          assertEquals(100, cba.last());
83          cba.addLast(getByteArrayFactory().create(100));
84          assertEquals(-100, cba.first());
85          assertEquals(200, cba.last());
86          cba.removeLast();
87          assertEquals(-100, cba.first());
88          assertEquals(100, cba.last());
89          cba.removeFirst();
90          assertEquals(0, cba.first());
91          assertEquals(100, cba.last());
92          cba.removeFirst();
93          assertEquals(100, cba.first());
94          assertEquals(100, cba.last());
95          cba.addLast(getByteArrayFactory().create(100));
96          assertEquals(100, cba.first());
97          assertEquals(200, cba.last());
98      }
99  
100     private BufferByteArray wrapString(String string) {
101         byte[] bytes = string.getBytes();
102         IoBuffer bb = IoBuffer.wrap(bytes);
103         BufferByteArray ba = new BufferByteArray(bb) {
104 
105             @Override
106             public void free() {
107                 addOperation(this + ".free()");
108                 // Nothing to do.
109             }
110 
111         };
112         return ba;
113     }
114 
115     private String toString(ByteArray ba) {
116         IoBuffer bb = IoBuffer.allocate(ba.length());
117         ba.get(0, bb);
118         byte[] bytes = bb.array();
119         String string = new String(bytes);
120         return string;
121     }
122 
123     public void testCompositeStringJoin() throws Exception {
124         ByteArray ba1 = wrapString("Hello");
125         ByteArray ba2 = wrapString("MINA");
126         ByteArray ba3 = wrapString("World");
127 
128         CompositeByteArray cba = new CompositeByteArray();
129         cba.addLast(ba1);
130         cba.addLast(ba2);
131         cba.addLast(ba3);
132 
133         assertEquals("HelloMINAWorld", toString(cba));
134     }
135 
136     public void testCompositeCursor() throws Exception {
137         IMocksControl mc = createStrictControl();
138 
139         ByteArray ba1 = getByteArrayFactory().create(10);
140         ByteArray ba2 = getByteArrayFactory().create(10);
141         ByteArray ba3 = getByteArrayFactory().create(10);
142 
143 
144         CompositeByteArray cba = new CompositeByteArray();
145         cba.addLast(ba1);
146         cba.addLast(ba2);
147         cba.addLast(ba3);
148 
149         CursorListener cl = mc.createMock(CursorListener.class);
150 
151         mc.reset();
152         mc.replay();
153         Cursor cursor = cba.cursor(cl);
154         mc.verify();
155 
156         mc.reset();
157         cl.enteredFirstComponent(0, ba1);
158         mc.replay();
159         cursor.get();
160         mc.verify();
161 
162         mc.reset();
163         mc.replay();
164         cursor.setIndex(10);
165         mc.verify();
166 
167         mc.reset();
168         cl.enteredNextComponent(10, ba2);
169         mc.replay();
170         cursor.put((byte) 55);
171         mc.verify();
172 
173         mc.reset();
174         mc.replay();
175         cursor.setIndex(9);
176         mc.verify();
177 
178         mc.reset();
179         cl.enteredPreviousComponent(0, ba1);
180         cl.enteredNextComponent(10, ba2);
181         mc.replay();
182         cursor.putInt(66);
183         mc.verify();
184 
185         mc.reset();
186         cl.enteredNextComponent(20, ba3);
187         mc.replay();
188         cursor.setIndex(29);
189         cursor.get();
190         mc.verify();
191 
192         cba.removeLast(); // Force cursor to relocate itself.
193 
194         mc.reset();
195         cl.enteredLastComponent(10, ba2);
196         mc.replay();
197         cursor.setIndex(15);
198         cursor.get();
199         mc.verify();
200 
201         mc.reset();
202         cl.enteredPreviousComponent(0, ba1);
203         mc.replay();
204         cursor.setIndex(0);
205         cursor.get();
206         mc.verify();
207     }
208 
209     public void testCompositeByteArray() throws Exception {
210         CompositeByteArray ba = new CompositeByteArray();
211         for (int i = 0; i < 1000; i += 100) {
212             ba.addLast(getByteArrayFactory().create(100));
213         }
214         resetOperations();
215         testAbsoluteReaderAndWriter(0, 1000, ba, ba);
216         testAbsoluteReaderAndWriter(0, 1000, ba, ba);
217         assertOperationCountEquals(0);
218         Cursor readCursor = ba.cursor();
219         Cursor writeCursor = ba.cursor();
220         testRelativeReaderAndWriter(1000, readCursor, writeCursor);
221         assertOperationCountEquals(0);
222     }
223 
224     public void testCompositeByteArrayRelativeReaderAndWriter() throws Exception {
225         CompositeByteArray cba = new CompositeByteArray();
226         CompositeByteArrayRelativeReader cbarr = new CompositeByteArrayRelativeReader(cba, true);
227         CompositeByteArrayRelativeWriter cbarw = new CompositeByteArrayRelativeWriter(cba, getExpander(100), getFlusher(), false);
228         resetOperations();
229         testRelativeReaderAndWriter(10, cbarr, cbarw);
230         assertOperationCountEquals(2);
231         resetOperations();
232         testRelativeReaderAndWriter(100, cbarr, cbarw);
233         assertOperationCountEquals(3);
234         resetOperations();
235         testRelativeReaderAndWriter(1000, cbarr, cbarw);
236         assertOperationCountEquals(30);
237         resetOperations();
238         testRelativeReaderAndWriter(10000, cbarr, cbarw);
239         assertOperationCountEquals(300);
240         resetOperations();
241         testRelativeReaderAndWriter(90, cbarr, cbarw);
242         assertOperationCountEquals(0); // Last free doesn't occur, since cursor only moves lazily.
243     }
244 
245     public void testCompositeByteArrayRelativeReaderAndWriterWithFlush() throws Exception {
246         CompositeByteArray cba = new CompositeByteArray();
247         CompositeByteArrayRelativeReader cbarr = new CompositeByteArrayRelativeReader(cba, true);
248         CompositeByteArrayRelativeWriter cbarw = new CompositeByteArrayRelativeWriter(cba, getExpander(100), getFlusher(), true);
249         resetOperations();
250         testRelativeReaderAndWriter(10, cbarr, cbarw);
251         assertOperationCountEquals(2);
252         resetOperations();
253         testRelativeReaderAndWriter(100, cbarr, cbarw);
254         assertOperationCountEquals(4);
255         resetOperations();
256         testRelativeReaderAndWriter(1000, cbarr, cbarw);
257         assertOperationCountEquals(40);
258         resetOperations();
259         testRelativeReaderAndWriter(10000, cbarr, cbarw);
260         assertOperationCountEquals(400);
261         resetOperations();
262         testRelativeReaderAndWriter(90, cbarr, cbarw);
263         assertOperationCountEquals(0); // Last free doesn't occur, since cursor only moves lazily.
264     }
265 
266     public void testCompositeRemoveTo() throws Exception {
267         CompositeByteArray cba = new CompositeByteArray();
268         {
269             // Remove nothing.
270             resetOperations();
271             ByteArray removed = cba.removeTo(0);
272             assertEquals(0, removed.first());
273             assertEquals(0, removed.last());
274             assertEquals(0, cba.first());
275             assertEquals(0, cba.last());
276             removed.free();
277             assertOperationCountEquals(0);
278         }
279         cba.addLast(getByteArrayFactory().create(100));
280         {
281             // Remove nothing.
282             resetOperations();
283             ByteArray removed = cba.removeTo(0);
284             assertEquals(0, removed.first());
285             assertEquals(0, removed.last());
286             assertEquals(0, cba.first());
287             assertEquals(100, cba.last());
288             removed.free();
289             assertOperationCountEquals(0);
290         }
291         {
292             // Remove entire component.
293             resetOperations();
294             ByteArray removed = cba.removeTo(100);
295             assertEquals(0, removed.first());
296             assertEquals(100, removed.last());
297             assertEquals(100, cba.first());
298             assertEquals(100, cba.last());
299             removed.free();
300             assertOperationCountEquals(1);
301         }
302         {
303             // Remove nothing.
304             resetOperations();
305             ByteArray removed = cba.removeTo(100);
306             assertEquals(0, removed.first());
307             assertEquals(0, removed.last());
308             assertEquals(100, cba.first());
309             assertEquals(100, cba.last());
310             removed.free();
311             assertOperationCountEquals(0);
312         }
313         cba.addLast(getByteArrayFactory().create(100));
314         {
315             // Remove nothing.
316             resetOperations();
317             ByteArray removed = cba.removeTo(100);
318             assertEquals(0, removed.first());
319             assertEquals(0, removed.last());
320             assertEquals(100, cba.first());
321             assertEquals(200, cba.last());
322             removed.free();
323             assertOperationCountEquals(0);
324         }
325         {
326             // Remove half a component.
327             resetOperations();
328             ByteArray removed = cba.removeTo(150);
329             assertEquals(0, removed.first());
330             assertEquals(50, removed.last());
331             assertEquals(150, cba.first());
332             assertEquals(200, cba.last());
333             removed.free();
334             assertOperationCountEquals(0); // Doesn't free until component finished.
335         }
336         {
337             // Remove nothing.
338             resetOperations();
339             ByteArray removed = cba.removeTo(150);
340             assertEquals(0, removed.first());
341             assertEquals(0, removed.last());
342             assertEquals(150, cba.first());
343             assertEquals(200, cba.last());
344             removed.free();
345             assertOperationCountEquals(0);
346         }
347         {
348             // Remove other half.
349             resetOperations();
350             ByteArray removed = cba.removeTo(200);
351             assertEquals(0, removed.first());
352             assertEquals(50, removed.last());
353             assertEquals(200, cba.first());
354             assertEquals(200, cba.last());
355             removed.free();
356             assertOperationCountEquals(1); // Frees ByteArray behind both buffers.
357         }
358     }
359     
360     public void testCompositeByteArraySlicing() {
361         CompositeByteArray cba = new CompositeByteArray();
362         cba.addLast(getByteArrayFactory().create(10));
363         cba.addLast(getByteArrayFactory().create(10));
364         cba.addLast(getByteArrayFactory().create(10));
365         testByteArraySlicing(cba, 0, 30);
366         testByteArraySlicing(cba, 5, 10);
367         testByteArraySlicing(cba, 10, 20);
368         testByteArraySlicing(cba, 1, 28);
369         testByteArraySlicing(cba, 19, 2);
370     }
371     
372     public void testBufferByteArraySlicing() {
373         ByteArray bba = getByteArrayFactory().create(30);
374         testByteArraySlicing(bba, 0, 30);
375         testByteArraySlicing(bba, 5, 10);
376         testByteArraySlicing(bba, 10, 20);
377         testByteArraySlicing(bba, 1, 28);
378         testByteArraySlicing(bba, 19, 2);
379         
380     }
381     
382     private void testByteArraySlicing(ByteArray ba, int start, int length) {
383         ByteArray slice = ba.slice(start, length);
384         for (int i = 0; i < length; i++) {
385             byte b1 = (byte) (i % 67);
386             byte b2 = (byte) (i % 36);
387             int sourceIndex = i + start;
388             int sliceIndex = i;
389             ba.put(sourceIndex, b1);
390             assertEquals(b1, ba.get(sourceIndex));
391             assertEquals(b1, slice.get(sliceIndex));
392             slice.put(sliceIndex, b2);
393             assertEquals(b2, ba.get(sourceIndex));
394             assertEquals(b2, slice.get(sliceIndex));
395         }
396     }
397 
398     private ChunkedExpander getExpander(final int chunkSize) {
399         return new ChunkedExpander(getByteArrayFactory(), chunkSize) {
400             @Override
401             public void expand(CompositeByteArray cba, int minSize) {
402                 addOperation("ChunkedExpander(" + chunkSize + ").expand(" + cba + "," + minSize + ")");
403                 super.expand(cba, minSize);
404             }
405         };
406     }
407 
408     private Flusher getFlusher() {
409         return new CompositeByteArrayRelativeWriter.Flusher() {
410 
411             public void flush(ByteArray ba) {
412                 addOperation("Flusher().flush(" + ba + ")");
413                 ba.free();
414             }
415 
416         };
417     }
418 
419     private SimpleByteArrayFactory getByteArrayFactory() {
420         return new SimpleByteArrayFactory() {
421             @Override
422             public ByteArray create(final int size) {
423                 if (size < 0) {
424                     throw new IllegalArgumentException(
425                             "Buffer size must not be negative:" + size);
426                 }
427                 IoBuffer bb = IoBuffer.allocate(size);
428                 ByteArray ba = new BufferByteArray(bb) {
429 
430                     @Override
431                     public void free() {
432                         addOperation(this + ".free()");
433                         // Nothing to do.
434                     }
435 
436                 };
437                 addOperation("SimpleByteArrayFactory().create(" + size + ") = " + ba);
438                 return ba;
439             }
440         };
441     }
442 
443     private void testRelativeReaderAndWriter(int length, IoRelativeReader reader, IoRelativeWriter writer) {
444         for (int i = 0; i < length; i++) {
445             byte b = (byte) (i % 67);
446             writer.put(b);
447             assertEquals(b, reader.get());
448         }
449     }
450 
451     private void testAbsoluteReaderAndWriter(int start, int length, IoAbsoluteReader reader, IoAbsoluteWriter writer) {
452         for (int i = start; i < length; i++) {
453             byte b = (byte) (i % 67);
454             writer.put(i, b);
455             assertEquals(b, reader.get(i));
456         }
457     }
458 
459     public void testByteArrayPrimitiveAccess() {
460         ByteArray bbaBig = getByteArrayFactory().create(1000);
461         bbaBig.order(ByteOrder.BIG_ENDIAN);
462         testPrimitiveAccess(bbaBig.cursor(), bbaBig.cursor());
463 
464         ByteArray bbaLittle = getByteArrayFactory().create(1000);
465         bbaLittle.order(ByteOrder.LITTLE_ENDIAN);
466         testPrimitiveAccess(bbaLittle.cursor(), bbaLittle.cursor());
467     }
468 
469     public void testByteArrayBufferAccess() {
470         ByteArray ba = getByteArrayFactory().create(1);
471         ba.put(0, (byte) 99);
472         IoBuffer bb = IoBuffer.allocate(2);
473         
474         bb.clear();
475         Cursor cursor = ba.cursor();
476         assertEquals(0, cursor.getIndex());
477         assertEquals(1, cursor.getRemaining());
478         assertEquals(0, bb.position());
479         assertEquals(2, bb.remaining());
480         cursor.get(bb);
481         assertEquals(1, cursor.getIndex());
482         assertEquals(0, cursor.getRemaining());
483         assertEquals(1, bb.position());
484         assertEquals(1, bb.remaining());
485     }
486     
487     public void testCompositeByteArrayPrimitiveAccess() {
488         CompositeByteArray cbaBig = new CompositeByteArray();
489         cbaBig.order(ByteOrder.BIG_ENDIAN);
490         for (int i = 0; i < 1000; i++) {
491             ByteArray component = getByteArrayFactory().create(1);
492             component.order(ByteOrder.BIG_ENDIAN);
493             cbaBig.addLast(component);
494         }
495         testPrimitiveAccess(cbaBig.cursor(), cbaBig.cursor());
496 
497         CompositeByteArray cbaLittle = new CompositeByteArray();
498         cbaLittle.order(ByteOrder.LITTLE_ENDIAN);
499         for (int i = 0; i < 1000; i++) {
500             ByteArray component = getByteArrayFactory().create(1);
501             component.order(ByteOrder.LITTLE_ENDIAN);
502             cbaLittle.addLast(component);
503         }
504         testPrimitiveAccess(cbaLittle.cursor(), cbaLittle.cursor());
505     }
506 
507     public void testCompositeByteArrayWrapperPrimitiveAccess() {
508         CompositeByteArray cbaBig = new CompositeByteArray();
509         cbaBig.order(ByteOrder.BIG_ENDIAN);
510         for (int i = 0; i < 1000; i++) {
511             ByteArray component = getByteArrayFactory().create(1);
512             component.order(ByteOrder.BIG_ENDIAN);
513             cbaBig.addLast(component);
514         }
515         testPrimitiveAccess(new CompositeByteArrayRelativeWriter(cbaBig, getExpander(10), getFlusher(), false), new CompositeByteArrayRelativeReader(cbaBig, true));
516 
517         CompositeByteArray cbaLittle = new CompositeByteArray();
518         cbaLittle.order(ByteOrder.LITTLE_ENDIAN);
519         for (int i = 0; i < 1000; i++) {
520             ByteArray component = getByteArrayFactory().create(1);
521             component.order(ByteOrder.LITTLE_ENDIAN);
522             cbaLittle.addLast(component);
523         }
524         testPrimitiveAccess(new CompositeByteArrayRelativeWriter(cbaLittle, getExpander(10), getFlusher(), false), new CompositeByteArrayRelativeReader(cbaLittle, true));
525     }
526 
527     private void testPrimitiveAccess(IoRelativeWriter write, IoRelativeReader read) {
528         byte b = (byte) 0x12;
529         write.put(b);
530         assertEquals(b, read.get());
531 
532         short s = (short) 0x12;
533         write.putShort(s);
534         assertEquals(s, read.getShort());
535 
536         int i = 0x12345678;
537         write.putInt(i);
538         assertEquals(i, read.getInt());
539 
540         long l = 0x1234567890123456L;
541         write.putLong(l);
542         assertEquals(l, read.getLong());
543 
544         float f = Float.intBitsToFloat(i);
545         write.putFloat(f);
546         assertEquals(f, read.getFloat());
547 
548         double d = Double.longBitsToDouble(l);
549         write.putDouble(d);
550         assertEquals(d, read.getDouble());
551 
552         char c = (char) 0x1234;
553         write.putChar(c);
554         assertEquals(c, read.getChar());
555     }
556 
557 }