From e911c3542122adc64ad929a3ab449ca6e3d9a2f7 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Thu, 23 Dec 2010 07:08:50 +0000 Subject: [PATCH] More NPOIFSFileSystem and NPOIFSStream read unit tests, along with details of a few more tests still to be written git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1052186 13f79535-47bb-0310-9956-ffa450edef68 --- .../poifs/filesystem/NPOIFSFileSystem.java | 17 +- .../poi/poifs/filesystem/NPOIFSStream.java | 21 +- .../filesystem/TestNPOIFSFileSystem.java | 30 +- .../poifs/filesystem/TestNPOIFSStream.java | 281 ++++++++++++++++++ 4 files changed, 343 insertions(+), 6 deletions(-) create mode 100644 src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSStream.java diff --git a/src/java/org/apache/poi/poifs/filesystem/NPOIFSFileSystem.java b/src/java/org/apache/poi/poifs/filesystem/NPOIFSFileSystem.java index 281474336..9a3aed8c9 100644 --- a/src/java/org/apache/poi/poifs/filesystem/NPOIFSFileSystem.java +++ b/src/java/org/apache/poi/poifs/filesystem/NPOIFSFileSystem.java @@ -653,12 +653,21 @@ public class NPOIFSFileSystem * we can bail out with an error rather than * spinning away for ever... */ - private class ChainLoopDetector { + protected class ChainLoopDetector { private boolean[] used_blocks; - private ChainLoopDetector() throws IOException { - used_blocks = new boolean[(int)(_data.size()/bigBlockSize.getBigBlockSize())]; + protected ChainLoopDetector() throws IOException { + int numBlocks = (int)Math.ceil(_data.size()/bigBlockSize.getBigBlockSize()); + used_blocks = new boolean[numBlocks]; } - private void claim(int offset) { + protected void claim(int offset) { + if(offset >= used_blocks.length) { + // They're writing, and have had new blocks requested + // for the write to proceed. That means they're into + // blocks we've allocated for them, so are safe + return; + } + + // Claiming an existing block, ensure there's no loop if(used_blocks[offset]) { throw new IllegalStateException( "Potential loop detected - Block " + offset + diff --git a/src/java/org/apache/poi/poifs/filesystem/NPOIFSStream.java b/src/java/org/apache/poi/poifs/filesystem/NPOIFSStream.java index ac9f918df..173953263 100644 --- a/src/java/org/apache/poi/poifs/filesystem/NPOIFSStream.java +++ b/src/java/org/apache/poi/poifs/filesystem/NPOIFSStream.java @@ -24,6 +24,7 @@ import java.nio.ByteBuffer; import java.util.Iterator; import org.apache.poi.poifs.common.POIFSConstants; +import org.apache.poi.poifs.filesystem.NPOIFSFileSystem.ChainLoopDetector; import org.apache.poi.poifs.property.Property; import org.apache.poi.poifs.storage.HeaderBlock; @@ -38,6 +39,8 @@ import org.apache.poi.poifs.storage.HeaderBlock; * This only works on big block streams, it doesn't * handle small block ones. * This uses the new NIO code + * + * TODO Add loop checking on read and on write */ public class NPOIFSStream implements Iterable @@ -100,11 +103,16 @@ public class NPOIFSStream implements Iterable // How many blocks are we going to need? int blocks = (int)Math.ceil(contents.length / filesystem.getBigBlockSize()); + // Make sure we don't encounter a loop whilst overwriting + // the existing blocks + ChainLoopDetector loopDetector = filesystem.new ChainLoopDetector(); + // Start writing int prevBlock = POIFSConstants.END_OF_CHAIN; int nextBlock = startBlock; for(int i=0; i prevBlock = thisBlock; } + // If we're overwriting, free any remaining blocks + // TODO + // Mark the end of the stream filesystem.setNextBlock(nextBlock, POIFSConstants.END_OF_CHAIN); } @@ -138,9 +149,16 @@ public class NPOIFSStream implements Iterable * Class that handles a streaming read of one stream */ protected class StreamBlockByteBufferIterator implements Iterator { + private ChainLoopDetector loopDetector; private int nextBlock; + protected StreamBlockByteBufferIterator(int firstBlock) { - nextBlock = firstBlock; + this.nextBlock = firstBlock; + try { + this.loopDetector = filesystem.new ChainLoopDetector(); + } catch(IOException e) { + throw new RuntimeException(e); + } } public boolean hasNext() { @@ -156,6 +174,7 @@ public class NPOIFSStream implements Iterable } try { + loopDetector.claim(nextBlock); ByteBuffer data = filesystem.getBlockAt(nextBlock); nextBlock = filesystem.getNextBlock(nextBlock); return data; diff --git a/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSFileSystem.java b/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSFileSystem.java index 62ce21a89..c511df374 100644 --- a/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSFileSystem.java +++ b/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSFileSystem.java @@ -17,6 +17,8 @@ package org.apache.poi.poifs.filesystem; +import java.nio.ByteBuffer; + import junit.framework.TestCase; import org.apache.poi.POIDataSamples; @@ -116,6 +118,32 @@ public final class TestNPOIFSFileSystem extends TestCase { * Check we get the right data back for each block */ public void testGetBlock() throws Exception { - // TODO + NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi")); + ByteBuffer b; + + // The 0th block is the first data block + b = fs.getBlockAt(0); + assertEquals((byte)0x9e, b.get()); + assertEquals((byte)0x75, b.get()); + assertEquals((byte)0x97, b.get()); + assertEquals((byte)0xf6, b.get()); + + // And the next block + b = fs.getBlockAt(1); + assertEquals((byte)0x86, b.get()); + assertEquals((byte)0x09, b.get()); + assertEquals((byte)0x22, b.get()); + assertEquals((byte)0xfb, b.get()); + + // Check the final block too + b = fs.getBlockAt(99); + assertEquals((byte)0x01, b.get()); + assertEquals((byte)0x00, b.get()); + assertEquals((byte)0x00, b.get()); + assertEquals((byte)0x00, b.get()); + assertEquals((byte)0x02, b.get()); + assertEquals((byte)0x00, b.get()); + assertEquals((byte)0x00, b.get()); + assertEquals((byte)0x00, b.get()); } } diff --git a/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSStream.java b/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSStream.java new file mode 100644 index 000000000..ef792e768 --- /dev/null +++ b/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSStream.java @@ -0,0 +1,281 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.poifs.filesystem; + +import java.nio.ByteBuffer; +import java.util.Iterator; + +import junit.framework.TestCase; + +import org.apache.poi.POIDataSamples; + +/** + * Tests {@link NPOIFSStream} + */ +public final class TestNPOIFSStream extends TestCase { + private static final POIDataSamples _inst = POIDataSamples.getPOIFSInstance(); + + /** + * Read a single block stream + */ + public void testReadTinyStream() throws Exception { + NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi")); + + // 98 is actually the last block in a two block stream... + NPOIFSStream stream = new NPOIFSStream(fs, 98); + Iterator i = stream.getBlockIterator(); + assertEquals(true, i.hasNext()); + assertEquals(true, i.hasNext()); + assertEquals(true, i.hasNext()); + ByteBuffer b = i.next(); + assertEquals(false, i.hasNext()); + assertEquals(false, i.hasNext()); + assertEquals(false, i.hasNext()); + + // Check the contents + assertEquals((byte)0x81, b.get()); + assertEquals((byte)0x00, b.get()); + assertEquals((byte)0x00, b.get()); + assertEquals((byte)0x00, b.get()); + assertEquals((byte)0x82, b.get()); + assertEquals((byte)0x00, b.get()); + assertEquals((byte)0x00, b.get()); + assertEquals((byte)0x00, b.get()); + } + + /** + * Read a stream with only two blocks in it + */ + public void testReadShortStream() throws Exception { + NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi")); + + // 97 -> 98 -> end + NPOIFSStream stream = new NPOIFSStream(fs, 97); + Iterator i = stream.getBlockIterator(); + assertEquals(true, i.hasNext()); + assertEquals(true, i.hasNext()); + assertEquals(true, i.hasNext()); + ByteBuffer b97 = i.next(); + assertEquals(true, i.hasNext()); + assertEquals(true, i.hasNext()); + ByteBuffer b98 = i.next(); + assertEquals(false, i.hasNext()); + assertEquals(false, i.hasNext()); + assertEquals(false, i.hasNext()); + + // Check the contents of the 1st block + assertEquals((byte)0x01, b97.get()); + assertEquals((byte)0x00, b97.get()); + assertEquals((byte)0x00, b97.get()); + assertEquals((byte)0x00, b97.get()); + assertEquals((byte)0x02, b97.get()); + assertEquals((byte)0x00, b97.get()); + assertEquals((byte)0x00, b97.get()); + assertEquals((byte)0x00, b97.get()); + + // Check the contents of the 2nd block + assertEquals((byte)0x81, b98.get()); + assertEquals((byte)0x00, b98.get()); + assertEquals((byte)0x00, b98.get()); + assertEquals((byte)0x00, b98.get()); + assertEquals((byte)0x82, b98.get()); + assertEquals((byte)0x00, b98.get()); + assertEquals((byte)0x00, b98.get()); + assertEquals((byte)0x00, b98.get()); + } + + /** + * Read a stream with many blocks + */ + public void testReadLongerStream() throws Exception { + NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi")); + + ByteBuffer b0 = null; + ByteBuffer b1 = null; + ByteBuffer b22 = null; + + // The stream at 0 has 23 blocks in it + NPOIFSStream stream = new NPOIFSStream(fs, 0); + Iterator i = stream.getBlockIterator(); + int count = 0; + while(i.hasNext()) { + ByteBuffer b = i.next(); + if(count == 0) { + b0 = b; + } + if(count == 1) { + b1 = b; + } + if(count == 22) { + b22 = b; + } + + count++; + } + assertEquals(23, count); + + // Check the contents + // 1st block is at 0 + assertEquals((byte)0x9e, b0.get()); + assertEquals((byte)0x75, b0.get()); + assertEquals((byte)0x97, b0.get()); + assertEquals((byte)0xf6, b0.get()); + + // 2nd block is at 1 + assertEquals((byte)0x86, b1.get()); + assertEquals((byte)0x09, b1.get()); + assertEquals((byte)0x22, b1.get()); + assertEquals((byte)0xfb, b1.get()); + + // last block is at 89 + assertEquals((byte)0xfe, b22.get()); + assertEquals((byte)0xff, b22.get()); + assertEquals((byte)0x00, b22.get()); + assertEquals((byte)0x00, b22.get()); + assertEquals((byte)0x05, b22.get()); + assertEquals((byte)0x01, b22.get()); + assertEquals((byte)0x02, b22.get()); + assertEquals((byte)0x00, b22.get()); + } + + /** + * Read a stream with several blocks in a 4096 byte block file + */ + public void testReadStream4096() throws Exception { + NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize4096.zvi")); + + // 0 -> 1 -> 2 -> end + NPOIFSStream stream = new NPOIFSStream(fs, 0); + Iterator i = stream.getBlockIterator(); + assertEquals(true, i.hasNext()); + assertEquals(true, i.hasNext()); + assertEquals(true, i.hasNext()); + ByteBuffer b0 = i.next(); + assertEquals(true, i.hasNext()); + assertEquals(true, i.hasNext()); + ByteBuffer b1 = i.next(); + assertEquals(true, i.hasNext()); + assertEquals(true, i.hasNext()); + ByteBuffer b2 = i.next(); + assertEquals(false, i.hasNext()); + assertEquals(false, i.hasNext()); + assertEquals(false, i.hasNext()); + + // Check the contents of the 1st block + assertEquals((byte)0x9E, b0.get()); + assertEquals((byte)0x75, b0.get()); + assertEquals((byte)0x97, b0.get()); + assertEquals((byte)0xF6, b0.get()); + assertEquals((byte)0xFF, b0.get()); + assertEquals((byte)0x21, b0.get()); + assertEquals((byte)0xD2, b0.get()); + assertEquals((byte)0x11, b0.get()); + + // Check the contents of the 2nd block + assertEquals((byte)0x00, b1.get()); + assertEquals((byte)0x00, b1.get()); + assertEquals((byte)0x03, b1.get()); + assertEquals((byte)0x00, b1.get()); + assertEquals((byte)0x00, b1.get()); + assertEquals((byte)0x00, b1.get()); + assertEquals((byte)0x00, b1.get()); + assertEquals((byte)0x00, b1.get()); + + // Check the contents of the 3rd block + assertEquals((byte)0x6D, b2.get()); + assertEquals((byte)0x00, b2.get()); + assertEquals((byte)0x00, b2.get()); + assertEquals((byte)0x00, b2.get()); + assertEquals((byte)0x03, b2.get()); + assertEquals((byte)0x00, b2.get()); + assertEquals((byte)0x46, b2.get()); + assertEquals((byte)0x00, b2.get()); + } + + /** + * Craft a nasty file with a loop, and ensure we don't get stuck + */ + public void testReadFailsOnLoop() throws Exception { + // TODO + } + + /** + * Writing the same amount of data as before + */ + public void testReplaceStream() throws Exception { + NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi")); + + // TODO + } + + /** + * Writes less data than before, some blocks will need + * to be freed + */ + public void testReplaceStreamWithLess() throws Exception { + NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi")); + + // TODO + } + + /** + * Writes more data than before, new blocks will be needed + */ + public void testReplaceStreamWithMore() throws Exception { + NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi")); + + // TODO + } + + /** + * Writes to a new stream in the file + */ + public void testWriteNewStream() throws Exception { + NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi")); + + // TODO + } + + /** + * Writes to a new stream in the file, where we've not enough + * free blocks so new FAT segments will need to be allocated + * to support this + */ + public void testWriteNewStreamExtraFATs() throws Exception { + NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi")); + + // TODO + } + + /** + * Replaces data in an existing stream, with a bit + * more data than before, in a 4096 byte block file + */ + public void testWriteStream4096() throws Exception { + NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize4096.zvi")); + + // TODO + } + + /** + * Craft a nasty file with a loop, and ensure we don't get stuck + */ + public void testWriteFailsOnLoop() throws Exception { + // TODO + } +}