/* * Microsoft JDBC Driver for SQL Server * * Copyright(c) Microsoft Corporation All rights reserved. * * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. */ package com.microsoft.sqlserver.testframework; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.sql.Blob; import java.sql.Clob; import java.sql.SQLException; import java.util.concurrent.ThreadLocalRandom; import java.util.logging.Logger; import com.microsoft.sqlserver.testframework.Utils.DBBinaryStream; import com.microsoft.sqlserver.testframework.Utils.DBCharacterStream; /** * Prepared invalid stream types */ public class DBInvalidUtil { public static final Logger log = Logger.getLogger("DBInvalidUtils"); /** * * InvalidClob : stream with invalid behavior 1) getCharacterStream returns InvalidCharacterStream 2) length returns invalid lengths * */ public class InvalidClob implements Clob { final int diff = 5; Object expected = null; public long length; private long actualLength; private long invalidLength = -1; private boolean returnValid = false; public InvalidCharacterStream stream = null; // keep a handle on any stream /** * Constructor * * @param expected * @param returnValid */ public InvalidClob(Object expected, boolean returnValid) { this.expected = expected; actualLength = this.value().length(); this.returnValid = returnValid; } protected String value() { return ((String) expected); } /** * return length */ public long length() throws SQLException { long ret = actualLength; long actual = ret; if (invalidLength == -1) { int choose = ThreadLocalRandom.current().nextInt(5); int randomInt = 1 + ThreadLocalRandom.current().nextInt(diff); switch (choose) { case 0: // more than ret actual = ret + randomInt; break; case 1: // less than ret actual = ret - randomInt; break; case 2: // 0 actual = 0; break; case 3: // return > SQL Server Limit actual = Long.MAX_VALUE; break; default: // always < -1 actual = -1 - randomInt; } invalidLength = actual; length = actual; returnValid = true; } log.fine("invalidClob.length(): Actual chars=" + actualLength + " Returned chars=" + length); return length; } public Reader getCharacterStream() throws SQLException { stream = new InvalidCharacterStream(this.value(), returnValid); return stream; } @Override public String getSubString(long pos, int length) throws SQLException { assertTrue(false, "Not implemented"); return null; } @Override public InputStream getAsciiStream() throws SQLException { assertTrue(false, "Not implemented"); return null; } @Override public long position(String searchstr, long start) throws SQLException { assertTrue(false, "Not implemented"); return 0; } @Override public long position(Clob searchstr, long start) throws SQLException { assertTrue(false, "Not implemented"); return 0; } @Override public int setString(long pos, String str) throws SQLException { assertTrue(false, "Not implemented"); return 0; } @Override public int setString(long pos, String str, int offset, int len) throws SQLException { assertTrue(false, "Not implemented"); return 0; } @Override public OutputStream setAsciiStream(long pos) throws SQLException { assertTrue(false, "Not implemented"); return null; } @Override public Writer setCharacterStream(long pos) throws SQLException { assertTrue(false, "Not implemented"); return null; } @Override public void truncate(long len) throws SQLException { assertTrue(false, "Not implemented"); } @Override public void free() throws SQLException { assertTrue(false, "Not implemented"); } @Override public Reader getCharacterStream(long pos, long length) throws SQLException { assertTrue(false, "Not implemented"); return null; } } /** * * invalidCharacterStream : stream with invalid behavior 1) Read can throw IOException 2) Read can return data length > or < than actual */ public class InvalidCharacterStream extends DBCharacterStream { final int diff = 5; private boolean returnValid = false; // Perfom invalid actions at most once public boolean threwException = false; public static final String IOExceptionMsg = "invalidCharacterStream.read() throws IOException"; // Constructor public InvalidCharacterStream(String value, boolean returnValid) { super(value); this.returnValid = returnValid; } public int read(char[] cbuf, int off, int len) throws IOException { int ret = super.read(cbuf, off, len); int actual = ret; if (!returnValid) { int choose = ThreadLocalRandom.current().nextInt(5); int randomInt = 1 + ThreadLocalRandom.current().nextInt(diff); switch (choose) { case 0: // more than ret actual = ret + randomInt; break; case 1: // less than ret actual = ret - randomInt; break; case 2: // 0 actual = 0; break; case 3: // always < -1 actual = -1 - randomInt; break; default: log.fine(IOExceptionMsg); threwException = true; throw new IOException(IOExceptionMsg); } returnValid = true; } log.fine("invalidCharacterStream.read(): Actual bytes=" + ret + " Returned bytes=" + actual); return actual; } } /** * InvalidBlob : stream with invalid behavior 1) getBinaryStream returns InvalidBinaryStream 2) Length returns invalid lengths */ public class InvalidBlob implements Blob { final int diff = 5; private Object expected = null; public long length; private long actualLength; private long invalidLength = -1; private boolean returnValid = false; private InvalidBinaryStream stream = null; // keep a handle on any stream // Constructor public InvalidBlob(Object expected, boolean returnValid) { this.expected = expected; actualLength = this.value().length; this.returnValid = returnValid; } protected byte[] value() { return ((byte[]) expected); } @Override public long length() throws SQLException { long ret = actualLength; long actual = ret; if (invalidLength == -1) { int choose = ThreadLocalRandom.current().nextInt(5); int randomInt = 1 + ThreadLocalRandom.current().nextInt(diff); switch (choose) { case 0: // more than ret actual = ret + randomInt; break; case 1: // less than ret actual = ret - randomInt; break; case 2: // 0 actual = 0; break; case 3: // return > SQL Server Limit actual = Long.MAX_VALUE; break; default: // always < -1 actual = -1 - randomInt; } invalidLength = actual; length = actual; returnValid = true; } log.fine("invalidBlob.length(): Actual bytes=" + actualLength + " Returned bytes=" + length); return length; } @Override public InputStream getBinaryStream() throws SQLException { stream = new InvalidBinaryStream(this.value(), returnValid); return stream; } @Override public byte[] getBytes(long pos, int length) throws SQLException { // TODO Auto-generated method stub return null; } @Override public long position(byte[] pattern, long start) throws SQLException { assertTrue(false, "Not implemented"); return 0; } @Override public long position(Blob pattern, long start) throws SQLException { assertTrue(false, "Not implemented"); return 0; } @Override public int setBytes(long pos, byte[] bytes) throws SQLException { assertTrue(false, "Not implemented"); return 0; } @Override public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException { assertTrue(false, "Not implemented"); return 0; } @Override public OutputStream setBinaryStream(long pos) throws SQLException { assertTrue(false, "Not implemented"); return null; } @Override public void truncate(long len) throws SQLException { assertTrue(false, "Not implemented"); } @Override public void free() throws SQLException { assertTrue(false, "Not implemented"); } @Override public InputStream getBinaryStream(long pos, long length) throws SQLException { assertTrue(false, "Not implemented"); return null; } } /** * invalidBinaryStream : stream with invalid behavior Read can return data length > or < than actual * */ public class InvalidBinaryStream extends DBBinaryStream { final int diff = 5; private boolean _returnValid = false; // Perfom invalid actions at most once /** * Constructor * * @param value * @param returnValid */ public InvalidBinaryStream(byte[] value, boolean returnValid) { super(value); _returnValid = returnValid; } @Override public int read(byte[] bytes, int off, int len) { int ret = super.read(bytes, off, len); int actual = ret; if (!_returnValid) { int choose = ThreadLocalRandom.current().nextInt(4); int randomInt = 1 + ThreadLocalRandom.current().nextInt(diff); switch (choose) { case 0: // greater than ret actual = ret + randomInt; break; case 1: // less than ret actual = ret - randomInt; break; case 2: // always < -1 actual = -1 - randomInt; break; default: // 0 actual = 0; } // Return invalid only once per stream _returnValid = true; } log.fine("invalidBinaryStream.read(): Actual bytes=" + ret + " Returned bytes=" + actual); return actual; } } }