/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.test.fs.driver.tests;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.jnode.driver.block.BlockDeviceAPI;
import org.jnode.driver.block.floppy.FloppyDriver;
import org.jnode.driver.block.ide.disk.IDEDiskDriver;
import org.jnode.driver.bus.ide.IDEConstants;
import org.jnode.test.fs.driver.BlockDeviceAPITestConfig;
import org.jnode.test.support.AbstractTest;
public class BlockDeviceAPITest extends AbstractTest {
public BlockDeviceAPITest() {
super(BlockDeviceAPITestConfig.class);
}
public BlockDeviceAPITest(String name) {
super(BlockDeviceAPITestConfig.class, name);
}
public void setUp() throws Exception {
super.setUp();
//put specific setUp here
}
/**
*
*/
public void tearDown() throws Exception {
//put specific tearDown here
super.tearDown();
}
public BlockDeviceAPI getBlockDeviceAPI() {
return ((BlockDeviceAPITestConfig) getTestConfig()).getBlockDeviceAPI();
}
public void testFlush() throws IOException {
getBlockDeviceAPI().flush();
}
public void testGetLength() throws IOException {
long length = getBlockDeviceAPI().getLength();
assertTrue("length must be > 0 (actual:" + length + ")", length > 0);
}
public void testRegularReadUnaligned() throws Exception {
doRead(false, Bounds.LOWER);
doRead(false, Bounds.NOMINAL);
doRead(false, Bounds.UPPER);
}
public void testRegularReadAligned() throws Exception {
doRead(true, Bounds.LOWER);
doRead(true, Bounds.NOMINAL);
doRead(true, Bounds.UPPER);
}
public void testOutOfBoundsRead() throws Exception {
doRead(true, Bounds.BEFORE_LOWER);
doRead(true, Bounds.AROUND_LOWER);
doRead(true, Bounds.AROUND_UPPER);
doRead(true, Bounds.AFTER_UPPER);
}
public void testRegularWriteUnaligned() throws Exception {
doWrite(false, Bounds.LOWER);
doWrite(false, Bounds.NOMINAL);
doWrite(false, Bounds.UPPER);
}
public void testRegularWriteAligned() throws Exception {
doWrite(true, Bounds.LOWER);
doWrite(true, Bounds.NOMINAL);
doWrite(true, Bounds.UPPER);
}
public void testOutOfBoundsWrite() throws Exception {
doWrite(true, Bounds.BEFORE_LOWER);
doWrite(true, Bounds.AROUND_LOWER);
doWrite(true, Bounds.AROUND_UPPER);
doWrite(true, Bounds.AFTER_UPPER);
}
private void doRead(boolean aligned, byte boundsType) throws Exception {
Bounds bounds = new Bounds(true, aligned, boundsType);
boolean errorOccured;
try {
doRead(bounds);
errorOccured = false;
} catch (Throwable t) {
if (!bounds.expectError()) {
log.error("Unexpected error occurred", t);
}
errorOccured = true;
}
if (bounds.expectError()) {
assertTrue("expected an error for " + bounds, errorOccured);
} else {
assertFalse("error not expected for " + bounds, errorOccured);
}
}
private void doWrite(boolean aligned, byte boundsType) throws Exception {
Bounds bounds = new Bounds(false, aligned, boundsType);
boolean errorOccured;
try {
doWrite(bounds);
errorOccured = false;
} catch (Throwable t) {
if (!bounds.expectError()) {
log.error("Unexpected error occurred", t);
}
errorOccured = true;
}
if (bounds.expectError()) {
assertTrue("expected an error for " + bounds, errorOccured);
} else {
assertFalse("error not expected for " + bounds, errorOccured);
}
}
private void doRead(Bounds bounds) throws IOException {
log.info(bounds.toString());
ByteBuffer bb = ByteBuffer.allocate(IDEConstants.SECTOR_SIZE);
long offset = bounds.getStart();
int toRead;
BlockDeviceAPI api = getBlockDeviceAPI();
while (offset < bounds.getEnd()) {
toRead = Math.min(bb.remaining(), (int) (bounds.getEnd() - offset));
bb.position(0).limit(toRead);
api.read(offset, bb);
bb.clear();
offset += toRead;
}
}
private void doWrite(Bounds bounds) throws IOException {
log.info(bounds.toString());
ByteBuffer bb = ByteBuffer.allocate(IDEConstants.SECTOR_SIZE);
long offset = bounds.getStart();
int toWrite;
BlockDeviceAPI api = getBlockDeviceAPI();
while (offset < bounds.getEnd()) {
toWrite = Math.min(bb.remaining(), (int) (bounds.getEnd() - offset));
bb.position(0).limit(toWrite);
api.write(offset, bb);
bb.clear();
offset += toWrite;
}
}
private class Bounds {
// bounds types
public static final byte BEFORE_LOWER = 0; // lead to an error
public static final byte AROUND_LOWER = 1; // lead to an error
public static final byte LOWER = 2;
public static final byte NOMINAL = 3;
public static final byte UPPER = 4;
public static final byte AROUND_UPPER = 5; // lead to an error
public static final byte AFTER_UPPER = 6; // lead to an error
private static final long UNALIGNMENT_OFFSET = IDEConstants.SECTOR_SIZE / 2;
private static final long DELTA = IDEConstants.SECTOR_SIZE;
private long start;
private long end;
private boolean expectError;
private boolean read;
private String toStringDesc = "";
public Bounds(boolean read, boolean aligned, byte boundsType) throws Exception {
this.read = read;
expectError = true;
toStringDesc = aligned ? "aligned " : "unaligned ";
long middle;
switch (boundsType) {
case BEFORE_LOWER:
toStringDesc += "BEFORE_LOWER";
expectError = true; // must give an error
start = -DELTA;
end = 0;
break;
case AROUND_LOWER:
toStringDesc += "AROUND_LOWER";
expectError = true; // must give an error
start = -DELTA;
end = +DELTA;
break;
case LOWER:
toStringDesc += "LOWER";
expectError = false; // must NOT give an error
start = 0;
end = +DELTA;
break;
case NOMINAL:
toStringDesc += "NOMINAL";
expectError = false; // must NOT give an error
middle = getBlockDeviceAPI().getLength() / 2;
start = middle - DELTA;
end = middle + DELTA;
break;
case UPPER:
toStringDesc += "UPPER";
expectError = false; // must NOT give an error
start = getBlockDeviceAPI().getLength() - DELTA;
end = getBlockDeviceAPI().getLength();
break;
case AROUND_UPPER:
toStringDesc += "AROUND_UPPER";
expectError = true; // must give an error
start = getBlockDeviceAPI().getLength() - DELTA;
end = getBlockDeviceAPI().getLength() + DELTA;
break;
case AFTER_UPPER:
toStringDesc += "AFTER_UPPER";
expectError = true; // must give an error
start = getBlockDeviceAPI().getLength();
end = getBlockDeviceAPI().getLength() + DELTA;
break;
default:
throw new Exception("unexpected boundsType: " + boundsType);
}
// is it a regular usage ?
if (!expectError) {
if (!aligned) {
start += UNALIGNMENT_OFFSET;
end += UNALIGNMENT_OFFSET;
}
// adjustment for regular usage (to be in the bounds)
start = Math.max(0, start);
end = Math.min(getBlockDeviceAPI().getLength(), end);
}
boolean apiNeedAlignment = (getBlockDeviceAPI() instanceof FloppyDriver) ||
(getBlockDeviceAPI() instanceof IDEDiskDriver);
expectError |= !aligned && apiNeedAlignment;
}
public long getEnd() {
return end;
}
public long getStart() {
return start;
}
public boolean expectError() {
return expectError;
}
public String toString() {
String devSize;
try {
devSize = "" + getBlockDeviceAPI().getLength();
} catch (IOException e) {
log.error("error in toString", e);
devSize = "???";
}
return (read ? "read " : "write ") + " " + toStringDesc +
" [" + start + ", " + end + "] (devSize=" + devSize + ") with config " + getTestConfig().getName();
}
}
}