package com.twmacinta.util.test;
import java.io.*;
import java.util.*;
import com.twmacinta.io.*;
import com.twmacinta.util.*;
/**
* Copyright (c) 2002 - 2010 by Timothy W Macinta, All Rights Reserved.<p>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* <p>
* 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
* Library General Public License for more details.
* <p>
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* <p>
* This class generates a stream of random data and compares the
* results of the md5 checksum generated by an instance of
* MD5OutputStream with the md5 checksum by piping the data through
* the local 'md5sum' or 'md5' binary.
* <p>
* See http://www.twmacinta.com/myjava/fast_md5.php for more information
* on this file.
*
* @author Tim Macinta (twm@alum.mit.edu)
**/
public class MD5OutputStreamTest {
/**
* Usage:
*
* java com.twmacinta.util.test.MD5OutputStreamTest [seed | "time" [max_data]]
*
**/
public static void main (String arg[]) {
try {
// determine random seed
long seed = System.currentTimeMillis();
if (arg.length > 0) {
if (!arg[0].equals("time")) {
seed = Long.parseLong(arg[0]);
}
}
// determine maximum size of data
long max_data = (20L * (1 << 30)); // max 20 gigabytes
if (arg.length > 1) {
max_data = Long.parseLong(arg[1]);
}
// report relevant system info
String[] propNames = {"os.name", "os.arch"};
for (int i = 0; i < propNames.length; i++) {
System.out.print(propNames[i]);
System.out.print(": ");
System.out.println(System.getProperty(propNames[i]));
}
// report whether native library is used
if (MD5.initNativeLibrary()) {
System.out.println("Successfully loaded native library");
} else {
System.out.println("WARNING: Native library NOT loaded");
}
// determine name of binary
String md5Binary = null;
String[] toTry = {"md5sum", "md5"};
for (int nameIndex = 0; nameIndex < toTry.length; nameIndex++) {
try {
String binName = toTry[nameIndex];
Process proc = Runtime.getRuntime().exec(binName);
proc.getOutputStream().close();
proc.getInputStream().close();
md5Binary = binName;
break;
} catch (Exception e) {}
}
if (md5Binary == null) throw new Exception("No md5sum binary or alternative found");
// repeatedly perform tests
Random ran = new Random(seed);
while (true) {
System.out.print("seed: "+seed+" \t");
long data_size = ran.nextLong();
if (data_size < 0) data_size = -data_size;
data_size = data_size % (max_data + 1);
System.out.println("size: "+data_size);
runTest(data_size, ran, md5Binary);
seed = ran.nextLong();
ran.setSeed(seed);
}
}
// handle exceptions
catch (Exception e) {
e.printStackTrace();
}
}
private static void runTest (long data_size, Random ran, String md5Binary) throws IOException {
Process proc = Runtime.getRuntime().exec(md5Binary);
MD5OutputStream out1 = new MD5OutputStream(new NullOutputStream());
OutputStream out2 = new BufferedOutputStream(proc.getOutputStream());
while (data_size > 0) {
int output_type = ran.nextInt() % 100;
output_type -= 5; // 5% chance
if (output_type < 0) {
outputSingleByte(ran, out1, out2);
data_size--;
continue;
}
output_type -= 25; // 25% chance
if (output_type < 0) {
data_size -= outputFullBuffer(ran, out1, out2, data_size);
continue;
}
// the default
data_size -= outputPartialBuffer(ran, out1, out2, data_size);
}
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
out2.flush();
out2.close();
String native_sum = new StringTokenizer(in.readLine()).nextToken();
in.close();
String java_sum = MD5.asHex(out1.hash());
if (!native_sum.equals(java_sum)) {
out1.close();
System.out.println("ERROR");
System.out.println("java: " + java_sum);
System.out.println("native: " + native_sum);
System.exit(1);
}
out1.close();
}
private static void outputSingleByte (Random ran, OutputStream out1, OutputStream out2) throws IOException {
int b = ran.nextInt() & 0xff;
out1.write(b);
out2.write(b);
}
private static long outputFullBuffer (Random ran, OutputStream out1, OutputStream out2, long max_bytes) throws IOException {
int b_len = ran.nextInt();
if (b_len < 0) b_len = -b_len;
b_len = b_len % ((1 << 20) / 8); // 1/8 meg max
if (b_len > max_bytes) b_len = (int) max_bytes;
byte[] b = new byte[b_len];
ran.nextBytes(b);
out1.write(b);
out2.write(b);
return b_len;
}
private static long outputPartialBuffer (Random ran, OutputStream out1, OutputStream out2, long max_bytes) throws IOException {
int b_len = ran.nextInt();
if (b_len < 0) b_len = -b_len;
b_len = b_len % ((1 << 20) / 2); // 1/2 meg max
if (b_len > max_bytes) b_len = (int) max_bytes;
if (b_len == 0) return 0;
byte[] b = new byte[b_len];
ran.nextBytes(b);
int off = ran.nextInt();
if (off < 0) off = -off;
off = off % b_len;
if (off == b_len) return 0;
int len = ran.nextInt();
if (len < 0) len = -len;
len = len % (b_len - off);
out1.write(b, off, len);
out2.write(b, off, len);
return len;
}
}