001    package org.apache.jcs.auxiliary.disk.block;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import java.io.File;
023    import java.util.Random;
024    
025    import junit.framework.TestCase;
026    
027    import org.apache.jcs.utils.serialization.StandardSerializer;
028    
029    /**
030     * Test for the disk access layer of the Block Disk Cache.
031     * <p>
032     * @author Aaron Smuts
033     */
034    public class BlockDiskUnitTest
035        extends TestCase
036    {
037        /** data file. */
038        private File rafDir;
039    
040        /**
041         * Creates the base directory
042         */
043        public BlockDiskUnitTest()
044        {
045            String rootDirName = "target/test-sandbox/block";
046            this.rafDir = new File( rootDirName );
047            this.rafDir.mkdirs();
048        }
049    
050        /**
051         * Test writing a null object within a single block size.
052         * <p>
053         * @throws Exception
054         */
055        public void testWrite_NullBlockElement()
056            throws Exception
057        {
058            // SETUP
059            String fileName = "testWrite_NullBlockElement";
060            File file = new File( rafDir, fileName + ".data" );
061            file.delete();
062            BlockDisk disk = new BlockDisk( file, new StandardSerializer() );
063    
064            // DO WORK
065            int[] blocks = disk.write( null );
066    
067            // VERIFY
068            System.out.println( "testWrite_NullBlockElement " + disk );
069            assertEquals( "Wrong number of blocks recorded.", 1, disk.getNumberOfBlocks() );
070            assertEquals( "Wrong number of blocks returned.", 1, blocks.length );
071            assertEquals( "Wrong block returned.", 0, blocks[0] );
072        }
073    
074        /**
075         * Test writing an element within a single block size.
076         * <p>
077         * @throws Exception
078         */
079        public void testWrite_SingleBlockElement()
080            throws Exception
081        {
082            // SETUP
083            String fileName = "testWrite_SingleBlockElement";
084            File file = new File( rafDir, fileName + ".data" );
085            file.delete();
086            BlockDisk disk = new BlockDisk( file, new StandardSerializer() );
087    
088            // DO WORK
089            int bytes = 1 * 1024;
090            int[] blocks = disk.write( new byte[bytes] );
091    
092            // VERIFY
093            System.out.println( "testWriteSingleBlockElement " + disk );
094            assertEquals( "Wrong number of blocks recorded.", 1, disk.getNumberOfBlocks() );
095            assertEquals( "Wrong number of blocks returned.", 1, blocks.length );
096            assertEquals( "Wrong block returned.", 0, blocks[0] );
097        }
098    
099        /**
100         * Test writing and reading an element within a single block size.
101         * <p>
102         * @throws Exception
103         */
104        public void testWriteAndRead_SingleBlockElement()
105            throws Exception
106        {
107            // SETUP
108            String fileName = "testWriteAndRead_SingleBlockElement";
109            File file = new File( rafDir, fileName + ".data" );
110            file.delete();
111            BlockDisk disk = new BlockDisk( file, new StandardSerializer() );
112    
113            // DO WORK
114            int bytes = 1 * 1024;
115            int[] blocks = disk.write( new byte[bytes] );
116    
117            byte[] result = (byte[]) disk.read( blocks );
118    
119            // VERIFY
120            assertEquals( "Wrong item retured.", new byte[bytes].length, result.length );
121        }
122    
123        /**
124         * Test writing two elements that each fit within a single block size.
125         * <p>
126         * @throws Exception
127         */
128        public void testWrite_TwoSingleBlockElements()
129            throws Exception
130        {
131            // SETUP
132            String fileName = "testWrite_TwoSingleBlockElements";
133            File file = new File( rafDir, fileName + ".data" );
134            file.delete();
135            BlockDisk disk = new BlockDisk( file, new StandardSerializer() );
136    
137            // DO WORK
138            int bytes = 1 * 1024;
139            int[] blocks1 = disk.write( new byte[bytes] );
140            int[] blocks2 = disk.write( new byte[bytes] );
141    
142            // VERIFY
143            assertEquals( "Wrong number of blocks recorded.", 2, disk.getNumberOfBlocks() );
144            assertEquals( "Wrong number of blocks returned.", 1, blocks1.length );
145            assertEquals( "Wrong block returned.", 0, blocks1[0] );
146            assertEquals( "Wrong number of blocks returned.", 1, blocks2.length );
147            assertEquals( "Wrong block returned.", 1, blocks2[0] );
148        }
149    
150        /**
151         * Verify that it says we need two blocks if the total size will fit.
152         * <p>
153         * @throws Exception
154         */
155        public void testCalculateBlocksNeededDouble()
156            throws Exception
157        {
158            // SETUP
159            String fileName = "testCalculateBlocksNeededDouble";
160            File file = new File( rafDir, fileName + ".data" );
161            file.delete();
162            BlockDisk disk = new BlockDisk( file, new StandardSerializer() );
163    
164            // DO WORK
165            int result = disk.calculateTheNumberOfBlocksNeeded( new byte[disk.getBlockSizeBytes() * 2
166                - ( 2 * BlockDisk.HEADER_SIZE_BYTES )] );
167    
168            // Verify
169            assertEquals( "Wrong number of blocks", 2, result );
170        }
171    
172        /**
173         * Test writing an element that takes two blocks.
174         * <p>
175         * @throws Exception
176         */
177        public void testWrite_DoubleBlockElement()
178            throws Exception
179        {
180            // SETUP
181            String fileName = "testWriteDoubleBlockElement";
182            File file = new File( rafDir, fileName + ".data" );
183            BlockDisk disk = new BlockDisk( file, new StandardSerializer() );
184    
185            // DO WORK
186            // byte arrays encur 27 bytes of serialization overhead.
187            int bytes = getBytesForBlocksOfByteArrays( disk.getBlockSizeBytes(), 2 );
188            int[] blocks = disk.write( new byte[bytes] );
189    
190            // VERIFY
191            System.out.println( "testWriteDoubleBlockElement " + disk );
192            assertEquals( "Wrong number of blocks recorded.", 2, disk.getNumberOfBlocks() );
193            assertEquals( "Wrong number of blocks returned.", 2, blocks.length );
194            assertEquals( "Wrong block returned.", 0, blocks[0] );
195        }
196        
197        /**
198         * Test writing an element that takes 128 blocks.  There was a byte in a for loop that limited the number to 127.  I fixed this.
199         * <p>
200         * @throws Exception
201         */
202        public void testWrite_128BlockElement()
203            throws Exception
204        {
205            // SETUP
206            int numBlocks = 128;
207            
208            String fileName = "testWrite_128BlockElement";
209            File file = new File( rafDir, fileName + ".data" );
210            BlockDisk disk = new BlockDisk( file, new StandardSerializer() );
211    
212            // DO WORK
213            // byte arrays encur 27 bytes of serialization overhead.
214            int bytes = getBytesForBlocksOfByteArrays( disk.getBlockSizeBytes(), numBlocks );
215            int[] blocks = disk.write( new byte[bytes] );
216    
217            // VERIFY
218            System.out.println( "testWriteDoubleBlockElement " + disk );
219            assertEquals( "Wrong number of blocks recorded.", numBlocks, disk.getNumberOfBlocks() );
220            assertEquals( "Wrong number of blocks returned.", numBlocks, blocks.length );
221            assertEquals( "Wrong block returned.", 0, blocks[0] );
222        }
223    
224        /**
225         * Test writing and reading elements that do not fit within a single block.
226         * <p>
227         * @throws Exception
228         */
229        public void testWriteAndReadMultipleMultiBlockElement()
230            throws Exception
231        {
232            // SETUP
233            String fileName = "testWriteAndReadSingleBlockElement";
234            File file = new File( rafDir, fileName + ".data" );
235            file.delete();
236            BlockDisk disk = new BlockDisk( file, new StandardSerializer() );
237    
238            // DO WORK
239            int numBlocksPerElement = 4;
240            int bytes = getBytesForBlocksOfByteArrays( disk.getBlockSizeBytes(), numBlocksPerElement );
241    
242            int numElements = 100;
243            for ( int i = 0; i < numElements; i++ )
244            {
245                int[] blocks = disk.write( new byte[bytes] );
246                byte[] result = (byte[]) disk.read( blocks );
247    
248                // VERIFY
249                assertEquals( "Wrong item retured.", new byte[bytes].length, result.length );
250                assertEquals( "Wrong number of blocks returned.", numBlocksPerElement, blocks.length );
251            }
252            System.out.println( "testWriteAndReadMultipleMultiBlockElement " + disk );
253        }
254    
255        /**
256         * Test writing and reading elements that do not fit within a single block.
257         * <p>
258         * @throws Exception
259         */
260        public void testWriteAndReadMultipleMultiBlockElement_setSize()
261            throws Exception
262        {
263            // SETUP
264            String fileName = "testWriteAndReadSingleBlockElement";
265            File file = new File( rafDir, fileName + ".data" );
266            file.delete();
267            int blockSizeBytes = 1024;
268            BlockDisk disk = new BlockDisk( file, blockSizeBytes );
269    
270            // DO WORK
271            int numBlocksPerElement = 4;
272            int bytes = getBytesForBlocksOfByteArrays( disk.getBlockSizeBytes(), numBlocksPerElement );
273    
274            int numElements = 100;
275            Random r = new Random(System.currentTimeMillis());
276            final byte[] src = new byte[bytes];
277            for ( int i = 0; i < numElements; i++ )
278            {
279                r.nextBytes(src);  // Ensure we don't just write zeros out
280                int[] blocks = disk.write( src );
281                byte[] result = (byte[]) disk.read( blocks );
282    
283                // VERIFY
284                assertEquals( "Wrong item length retured.", src.length, result.length );
285                assertEquals( "Wrong number of blocks returned.", numBlocksPerElement, blocks.length );
286    
287                // We check the array contents, too, to ensure we read back what we wrote out
288                for (int j = 0 ; j < src.length ; j++) {
289                    assertEquals( "Mismatch at offset " + j + " in attempt # " + (i + 1), src[j], result[j] );
290                }
291            }
292            System.out.println( "testWriteAndReadMultipleMultiBlockElement_setSize " + disk );
293            assertEquals( "Wrong number of elements.", numBlocksPerElement * numElements, disk.getNumberOfBlocks() );
294        }
295    
296        /**
297         * Used to get the size for byte arrays that will take up the number of blocks specified.
298         * <p>
299         * @param blockSize
300         * @param numBlocks
301         * @return num bytes.
302         */
303        private int getBytesForBlocksOfByteArrays( int blockSize, int numBlocks )
304        {
305            // byte arrays encur some bytes of serialization overhead.
306            return blockSize * numBlocks - ( numBlocks * BlockDisk.HEADER_SIZE_BYTES ) - ( numBlocks * 14 );
307        }
308    
309        /**
310         * Verify that the block disk can handle a big string.
311         * <p>
312         * @throws Exception
313         */
314        public void testWriteAndRead_BigString()
315            throws Exception
316        {
317            // SETUP
318            String fileName = "testWriteAndRead_BigString";
319            File file = new File( rafDir, fileName + ".data" );
320            file.delete();
321            int blockSizeBytes = 4096;//1024;
322            BlockDisk disk = new BlockDisk( file, blockSizeBytes, new StandardSerializer() );
323    
324            String string = "This is my big string ABCDEFGH";
325            StringBuffer sb = new StringBuffer();
326            sb.append( string );
327            for ( int i = 0; i < 8; i++ )
328            {
329                sb.append( " " + i + sb.toString() ); // big string
330            }
331            string = sb.toString();
332    
333            // DO WORK
334            int[] blocks = disk.write( string );
335            String result = (String) disk.read( blocks );
336    
337            // VERIFY
338            System.out.println( string );
339            System.out.println( result );
340            System.out.println( disk );
341            assertEquals( "Wrong item retured.", string, result );
342        }
343    
344        /**
345         * Verify that the block disk can handle a big string.
346         * <p>
347         * @throws Exception
348         */
349        public void testWriteAndRead_BigString2()
350            throws Exception
351        {
352            // SETUP
353            String fileName = "testWriteAndRead_BigString";
354            File file = new File( rafDir, fileName + ".data" );
355            file.delete();
356            int blockSizeBytes = 47;//4096;//1024;
357            BlockDisk disk = new BlockDisk( file, blockSizeBytes, new StandardSerializer() );
358    
359            String string = "abcdefghijklmnopqrstuvwxyz1234567890";
360            string += string;
361            string += string;
362    
363            // DO WORK
364            int[] blocks = disk.write( string );
365            String result = (String) disk.read( blocks );
366    
367            // VERIFY 
368            assertEquals( "Wrong item retured.", string, result );
369        }
370    }