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