content: /*
   * © 2017 AgNO3 Gmbh & Co. KG
   * 
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   * 
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   * 
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  package jcifs.tests;
  
  
  import static org.junit.Assert.assertArrayEquals;
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.fail;
  
  import java.io.DataOutput;
  import java.io.IOException;
  import java.io.InputStream;
  import java.net.MalformedURLException;
  import java.net.UnknownHostException;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.Map;
  import java.util.Random;
  
  import org.junit.Assert;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  import org.junit.runners.Parameterized;
  import org.junit.runners.Parameterized.Parameters;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import jcifs.CIFSException;
  import jcifs.SmbRandomAccess;
  import jcifs.smb.SmbEndOfFileException;
  import jcifs.smb.SmbFile;
  import jcifs.smb.SmbFileOutputStream;
  import jcifs.smb.SmbRandomAccessFile;
  
  
  /**
   * @author mbechler
   *
   */
  @RunWith ( Parameterized.class )
  @SuppressWarnings ( "javadoc" )
  public class RandomAccessFileTest extends BaseCIFSTest {
  
      private static final Logger log = LoggerFactory.getLogger(RandomAccessFileTest.class);
  
  
      /**
       * @param name
       * @param properties
       */
      public RandomAccessFileTest ( String name, Map<String, String> properties ) {
          super(name, properties);
      }
  
  
      @Parameters ( name = "{0}" )
      public static Collection<Object> configs () {
          return getConfigs("smb1", "noUnicode", "forceUnicode", "noNTStatus", "noNTSmbs", "smb2", "smb30", "smb31");
      }
  
  
      @Test
      public void testReadOnly () throws IOException {
          try ( SmbFile f = createTestFile() ) {
              try {
                  try ( SmbFileOutputStream os = f.openOutputStream() ) {
                      os.write(new byte[] {
                          0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7
                      });
                  }
  
                  byte[] buf = new byte[4];
                  try ( SmbRandomAccessFile raf = new SmbRandomAccessFile(f, "r") ) {
                      raf.seek(4);
                      raf.readFully(buf);
                  }
  
                  Assert.assertArrayEquals(new byte[] {
                      0x4, 0x5, 0x6, 0x7
                  }, buf);
              }
              finally {
                  f.delete();
              }
          }
      }
  
  
      @Test
      public void testReadOnlySeekOOB () throws IOException {
          try ( SmbFile f = createTestFile() ) {
              try {
                  try ( SmbFileOutputStream os = f.openOutputStream() ) {
                      os.write(new byte[] {
                          0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7
                      });
                  }
  
                  try ( SmbRandomAccessFile raf = new SmbRandomAccessFile(f, "r") ) {
                      raf.seek(10);
                      Assert.assertEquals(-1, raf.read());
                  }
  
                  byte[] buf = new byte[4];
                  try ( SmbRandomAccessFile raf = new SmbRandomAccessFile(f, "r") ) {
                      raf.seek(6);
                      Assert.assertEquals(2, raf.read(buf));
                      Assert.assertArrayEquals(new byte[] {
                          0x6, 0x7, 0x0, 0x0
                      }, buf);
                  }
  
                  try ( SmbRandomAccessFile raf = new SmbRandomAccessFile(f, "r") ) {
                      raf.seek(6);
                      try {
                          raf.readFully(buf);
                          Assert.fail("Should have thrown exception");
                      }
                      catch ( SmbEndOfFileException e ) {}
                  }
  
              }
              finally {
                  f.delete();
              }
          }
      }
  
  
      @Test
      public void testSetLength () throws CIFSException, MalformedURLException, UnknownHostException {
          try ( SmbFile f = createTestFile() ) {
              try {
                  long newLength = 4096L;
                  try ( SmbRandomAccessFile raf = new SmbRandomAccessFile(f, "rw") ) {
                      raf.setLength(newLength);
                  }
                  assertEquals(newLength, f.length());
              }
              finally {
                  f.delete();
              }
          }
      }
  
  
      @Test
      public void testSetLengthTruncate () throws CIFSException, MalformedURLException, UnknownHostException {
          try ( SmbFile f = createTestFile() ) {
              try {
                  long newLength = 1024L;
                  try ( SmbRandomAccessFile raf = new SmbRandomAccessFile(f, "rw") ) {
                      raf.seek(4096);
                      raf.write(0);
                      raf.setLength(newLength);
                  }
                  assertEquals(newLength, f.length());
              }
              finally {
                  f.delete();
              }
          }
      }
  
  
      @Test
      public void testReadWrite () throws IOException {
          try ( SmbFile f = createTestFile() ) {
              try {
                  int bufSize = 4096;
                  long l = 1024;
                  try ( SmbRandomAccessFile raf = new SmbRandomAccessFile(f, "rw") ) {
                      writeRandom(bufSize, l, raf);
                      assertEquals(l, raf.getFilePointer());
                      raf.seek(0);
                      assertEquals(0, raf.getFilePointer());
                      verifyRandom(bufSize, l, raf);
                      assertEquals(l, raf.getFilePointer());
                  }
                  assertEquals(l, f.length());
              }
              finally {
                  f.delete();
              }
          }
      }
  
  
      @Test
      public void testReadWriteSeeked () throws IOException {
          try ( SmbFile f = createTestFile() ) {
              try {
                  int bufSize = 4096;
                  long l = 1024;
                  int off = 2048;
                  try ( SmbRandomAccessFile raf = new SmbRandomAccessFile(f, "rw") ) {
                      raf.seek(off);
                      writeRandom(bufSize, l, raf);
                      assertEquals(l + off, raf.getFilePointer());
                      raf.seek(off);
                      assertEquals(off, raf.getFilePointer());
                      verifyRandom(bufSize, l, raf);
                      assertEquals(l + off, raf.getFilePointer());
                  }
                  assertEquals(l + off, f.length());
              }
              finally {
                  f.delete();
              }
          }
      }
  
  
      @Test
      public void testWriteVerify () throws IOException {
          try ( SmbFile f = createTestFile() ) {
              try {
                  int bufSize = 4096;
                  int l = 1024;
                  int off = 2048;
                  try ( SmbRandomAccessFile raf = new SmbRandomAccessFile(f, "rw") ) {
                      raf.seek(off);
                      writeRandom(bufSize, l, raf);
                      assertEquals(l + off, raf.getFilePointer());
                      raf.setLength(raf.getFilePointer() + l);
                  }
  
                  try ( InputStream is = f.getInputStream() ) {
                      verifyZero(off, is);
                      ReadWriteTest.verifyRandom(bufSize, l, false, is);
                      verifyZero(l, is);
                  }
              }
              finally {
                  f.delete();
              }
          }
      }
  
  
      @Test
      public void testLargeReadWrite () throws IOException {
          try ( SmbFile f = createTestFile() ) {
              try {
                  int bufSize = 64 * 1024 + 512;
                  int l = bufSize;
                  try ( SmbRandomAccess raf = f.openRandomAccess("rw") ) {
                      writeRandom(bufSize, l, raf);
                  }
  
                  try ( SmbRandomAccess raf = f.openRandomAccess("rw") ) {
                      verifyRandom(bufSize, l, raf);
                  }
              }
              finally {
                  f.delete();
              }
          }
      }
  
  
      @Test
      public void testReadFullyMultipleWrites () throws IOException {
          try ( SmbFile f = createTestFile() ) {
  
              try ( SmbRandomAccess raf = f.openRandomAccess("rw") ) {
  
                  raf.write(new byte[] {
                      0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8
                  });
                  raf.seek(0);
                  assertEquals(0, raf.getFilePointer());
  
                  byte[] buf = new byte[4];
  
                  raf.readFully(buf);
                  assertArrayEquals(new byte[] {
                      0x1, 0x2, 0x3, 0x4
                  }, buf);
                  assertEquals(4, raf.getFilePointer());
  
                  raf.readFully(buf);
                  assertArrayEquals(new byte[] {
                      0x5, 0x6, 0x7, 0x8
                  }, buf);
                  assertEquals(8, raf.getFilePointer());
              }
          }
      }
  
  
      private static void verifyZero ( int cnt, InputStream is ) throws IOException {
          byte[] offBuf = new byte[cnt];
          int pos = 0;
          while ( pos < cnt ) {
              int r = is.read(offBuf, pos, offBuf.length - pos);
              if ( r < 0 ) {
                  fail("EOF while reading");
              }
              pos += r;
          }
  
          for ( int i = 0; i < offBuf.length; i++ ) {
              if ( offBuf[ i ] != 0 ) {
                  fail("Not zero @ " + i);
              }
          }
      }
  
  
      /**
       * @param bufSize
       * @param length
       * @param is
       * @throws IOException
       */
      static void verifyRandom ( int bufSize, long length, SmbRandomAccess is ) throws IOException {
          long start = System.currentTimeMillis();
          byte buffer[] = new byte[bufSize];
          long p = 0;
          Random r = ReadWriteTest.getRandom();
          while ( p < length ) {
  
              int rs = Math.min(bufSize, (int) ( length - p ));
              int read = is.read(buffer, 0, rs);
              if ( read < 0 ) {
                  fail("Unexpected EOF");
              }
  
              byte verify[] = new byte[read];
              ReadWriteTest.randBytes(r, verify);
              byte actual[] = Arrays.copyOfRange(buffer, 0, read);
  
              assertArrayEquals("Data matches at offset " + p, actual, verify);
  
              p += read;
          }
          assertEquals("Expecting EOF", -1, is.read(buffer, 0, 1));
          log.debug("Read " + length + " took " + ( System.currentTimeMillis() - start ));
      }
  
  
      /**
       * @param bufSize
       * @param length
       * @param os
       * @throws IOException
       */
      static void writeRandom ( int bufSize, long length, DataOutput os ) throws IOException {
          long start = System.currentTimeMillis();
          byte buffer[] = new byte[bufSize];
          long p = 0;
          Random r = ReadWriteTest.getRandom();
          while ( p < length ) {
              ReadWriteTest.randBytes(r, buffer);
              int w = Math.min(bufSize, (int) ( length - p ));
              os.write(buffer, 0, w);
              p += w;
          }
          log.debug("Write " + length + " took " + ( System.currentTimeMillis() - start ));
      }
  }
  