/* * Copyright (c) 2003-2012 Fred Hutchinson Cancer Research Center * * Licensed 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.fhcrc.cpl.toolbox.test; import java.io.File; import java.io.FileInputStream; import java.io.PrintWriter; import java.io.FileOutputStream; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Date; import org.fhcrc.cpl.toolbox.ApplicationContext; /** * Abstract base class for all msInspect tests. Lots of utility methods */ public class TestUtilities { //Strings to store paths for log, temp files, etc protected static String mViewerRootDirName = null; protected static String mSampleDataDirName = null; protected static String mRootTestTempfileDirName = null; protected static String mRootTestDirName = null; protected static String mTestLogDirName = null; //Variables related to the log file protected static File mLogFile = null; protected static PrintWriter mLogPrintWriter = null; protected static boolean mTriedToOpenLog = false; public TestUtilities() { } /** * Recursively delete a file and any children from the filesystem * @param file */ public static void recursiveFileDelete(File file) { if (file.isDirectory()) { for (File childFile : file.listFiles()) { recursiveFileDelete(childFile); } } file.delete(); } /* * Methods to deal with temp files */ /** * Create a temp dir for this test * @return */ public static boolean createTestTempDir(String className) { try { File testTempDir = new File(getCurrentTestTempDirName(className)); if (!testTempDir.exists()) { if (!testTempDir.mkdirs()) return false; } } catch (Exception e) { return false; } return true; } /** * Get the directory name of the sampledata directory, where all files that the test might need * to work with should live * @return */ public static String getSampleDataDirName() { if (mSampleDataDirName == null) mSampleDataDirName = getViewerRootDirName() + System.getProperty("file.separator") + "sampledata"; return mSampleDataDirName; } /** * Convenience method to get the filepath of a named file in the sampledata dir * @param fileName * @return */ public static String getSampleDataDirFilePath(String fileName) { return getSampleDataDirName() + System.getProperty("file.separator") + fileName; } /** * Get the viewer root directory (webapps/tools) * @return */ public static String getViewerRootDirName() { if (mViewerRootDirName == null) mViewerRootDirName = System.getProperty("viewer.root"); return mViewerRootDirName; } /** * webapps/tools/test. Contains logs and temp_files subdirs * @return */ public static String getRootTestDirName() { if (mRootTestDirName == null) mRootTestDirName = getViewerRootDirName() + System.getProperty("file.separator") + "test"; return mRootTestDirName; } /** * * @return directory name of the test log directory */ public static String getTestLogDirName() { if (mTestLogDirName == null) mTestLogDirName = getRootTestDirName() + System.getProperty("file.separator") + "logs"; return mTestLogDirName; } /** * * @return dir name of the root test temp file directory */ public static String getRootTestTempfileDirName() { if (mRootTestTempfileDirName == null) mRootTestTempfileDirName = getRootTestDirName() + System.getProperty("file.separator") + "temp_files"; return mRootTestTempfileDirName; } /** * * @return dir name of the temp file dir for this test */ public static String getCurrentTestTempDirName(String className) { return getRootTestTempfileDirName() + System.getProperty("file.separator") + className; } /** * Convenience method to construct a filepath in the temp file dir * @param relativeFilePath * @return */ public static String constructTempFilePath(String className, String relativeFilePath) { return getCurrentTestTempDirName(className) + System.getProperty("file.separator") + relativeFilePath; } /* * Methods related to MD5 hashes. We use MD5 hashes for results verification when we can be * absolutely sure that succeeding in a test requires the file to be exactly the same as a * file generated when the test is created, MODULO WHITESPACE. We strip whitespace before * comparing the files * */ /** * Convert a byte array to a hex string * @param v * @return */ public static String toHexString (byte [] v) { StringBuffer sb = new StringBuffer (); byte n1, n2; for (int c = 0; c < v.length; c++) { n1 = (byte)((v[c] < 0 ? -v[c] + 127 : v[c]) / 0x10); n2 = (byte)((v[c] < 0 ? -v[c] + 127 : v[c]) % 0x10); sb.append (n1 >= 0xA ? (char)(n1 - 0xA + 'a') : (char)(n1 + '0')); sb.append (n2 >= 0xA ? (char)(n2 - 0xA + 'a') : (char)(n2 + '0')); } return sb.toString(); } /** * Strip # style comments from a file. Useful for creating md5 sums for files in which the * comments might vary but everything else must be the same * @param file * @throws Exception */ public static void removeHashCommentLinesFromFile(File file) throws Exception { ArrayList<Byte> fileBytesList = new ArrayList<Byte>(); FileInputStream fis = new FileInputStream(file); boolean isLineStart = true; boolean inComment = false; while (true) { byte nextChar = (byte) fis.read(); if (nextChar == -1) break; if (inComment) { if (nextChar == '\n') { inComment = false; isLineStart = true; } continue; } if (isLineStart) { isLineStart = false; if (nextChar == '#') { inComment = true; continue; } } fileBytesList.add(nextChar); } fis.close(); file.delete(); FileOutputStream fos = new FileOutputStream(file); for (int i=0; i<fileBytesList.size(); i++) { fos.write(fileBytesList.get(i)); } fos.flush(); fos.close(); } public static String calculateHexMD5SumNoWhiteSpaceNoComments(File file) throws Exception { removeHashCommentLinesFromFile(file); return calculateHexMD5SumNoWhiteSpace(file); } /** * Calculate an MD5 sum for a file, first stripping out all whitespace, then convert it to hex * @param file * @return * @throws Exception */ public static String calculateHexMD5SumNoWhiteSpace(File file) throws Exception { ArrayList<Byte> fileBytesList = new ArrayList<Byte>(); FileInputStream fis = new FileInputStream(file); int realchars=0; while (true) { byte nextChar = (byte) fis.read(); if (nextChar == -1) break; if (!Character.isWhitespace(nextChar)) { //if (Character.isLetterOrDigit(nextChar)) //{ // System.err.println(nextChar); // realchars++; //} fileBytesList.add(nextChar); } } MessageDigest md5Digest = MessageDigest.getInstance("MD5"); byte[] fileBytes = new byte[fileBytesList.size()]; //System.err.println("stripped file is " + fileBytes.length + " bytes, with " + realchars + " letters and digits"); for (int i=0; i<fileBytesList.size(); i++) fileBytes[i] = fileBytesList.get(i); return toHexString(md5Digest.digest(fileBytes)); } /** * Compare the MD5 sum of a file with a previously stored sum * @param file * @param targetSum * @return * @throws Exception */ public static boolean compareHexMD5SumsNoWhiteSpace(File file, String targetSum) throws Exception { String fileSum = calculateHexMD5SumNoWhiteSpace(file); if (fileSum.equals(targetSum)) return true; //if we got here, failed log("Failed MD5 sum comparison for file " + file.getAbsolutePath()); log("File exists? " + file.exists()); log("Target sum: " + targetSum); log("Calculated sum: " + fileSum); return false; } public static boolean compareHexMD5SumsNoWhiteSpaceNoComments(File file, String targetSum) throws Exception { removeHashCommentLinesFromFile(file); return compareHexMD5SumsNoWhiteSpace(file,targetSum); } /** * Write a lot message to System.err and also to a log file * @param mesg */ public static void log(String mesg) { Date date = new Date(); //yeah, yeah, these methods are deprecated. They'll be around forever. String minutes = "" + date.getMinutes(); if (date.getMinutes() < 10) minutes = "0" + minutes; String seconds = "" + date.getSeconds(); if (date.getSeconds() < 10) seconds = "0" + seconds; mesg = "[" + date.getHours() + ":" + minutes + ":" + seconds + "] " + mesg; ApplicationContext.infoMessage(mesg); if (mLogPrintWriter != null) { mLogPrintWriter.println(mesg); mLogPrintWriter.flush(); } else { if (!mTriedToOpenLog) { openLogPrintWriter(); if (mLogPrintWriter != null) mLogPrintWriter.println(mesg); } } } /** * Manage the log file * @return */ public static File getLogFile() { if (mLogFile == null) { File logDirectory = new File(getTestLogDirName()); if (!logDirectory.exists()) logDirectory.mkdirs(); mLogFile = new File(getTestLogDirName() + System.getProperty("file.separator") + "msinspect_test.log"); } return mLogFile; } /** * Open a PrintWriter on the log file */ public static void openLogPrintWriter() { if (mLogPrintWriter == null && !mTriedToOpenLog) { try { mTriedToOpenLog = true; mLogPrintWriter = new PrintWriter(getLogFile()); } catch (Exception e) { System.err.println("Error opening log file for writing, logs will only be written to screen. Error message: " + e.getMessage()); } } } /** * Close the PrintWriter on the log file, if it's open */ public static void closeLog() { if (mLogPrintWriter != null) mLogPrintWriter.close(); } }