From 5d4dc02634457724f2e507d14b72d95ef2adf902 Mon Sep 17 00:00:00 2001 From: Dick Fickling <dick@fickling.us> Date: Mon, 21 May 2012 10:10:56 -0400 Subject: [PATCH] Initial test script commit. --- .gitignore | 4 + .../hashdatabase/HashDbMgmtAction.java | 2 +- .../autopsy/hashdatabase/HashDbMgmtPanel.java | 22 +- Testing/build.xml | 71 +++++ Testing/manifest.mf | 5 + Testing/nbproject/build-impl.xml | 45 ++++ Testing/nbproject/genfiles.properties | 8 + Testing/nbproject/project.properties | 2 + Testing/nbproject/project.xml | 56 ++++ Testing/nbproject/suite.properties | 1 + Testing/script/getcounts.py | 38 +++ Testing/script/regression.py | 247 ++++++++++++++++++ .../autopsy/testing/Bundle.properties | 1 + .../autopsy/testing/RegressionTest.java | 215 +++++++++++++++ nbproject/platform.properties | 1 + nbproject/project.properties | 6 +- 16 files changed, 718 insertions(+), 6 deletions(-) create mode 100644 Testing/build.xml create mode 100644 Testing/manifest.mf create mode 100644 Testing/nbproject/build-impl.xml create mode 100644 Testing/nbproject/genfiles.properties create mode 100644 Testing/nbproject/project.properties create mode 100644 Testing/nbproject/project.xml create mode 100644 Testing/nbproject/suite.properties create mode 100644 Testing/script/getcounts.py create mode 100644 Testing/script/regression.py create mode 100644 Testing/src/org/sleuthkit/autopsy/testing/Bundle.properties create mode 100644 Testing/test/qa-functional/src/org/sleuthkit/autopsy/testing/RegressionTest.java diff --git a/.gitignore b/.gitignore index a9b2026f25..8a6216b27d 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,7 @@ Bundle_*.properties /branding/core/core.jar/org/netbeans/core/startup/Bundle.properties /branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties /CoreUtils/src/org/sleuthkit/autopsy/coreutils/Bundle.properties +/Testing/script/input/ +/Testing/script/output/ +/Testing/script/gold/ +*~ diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbMgmtAction.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbMgmtAction.java index cd134795e3..b0d8b3c7bc 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbMgmtAction.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbMgmtAction.java @@ -32,7 +32,7 @@ */ class HashDbMgmtAction extends CallableSystemAction { - private static final String ACTION_NAME = "Hash Database Configuration"; + static final String ACTION_NAME = "Hash Database Configuration"; @Override public void performAction() { diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbMgmtPanel.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbMgmtPanel.java index 216aa8dcf8..b3bf5254ec 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbMgmtPanel.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbMgmtPanel.java @@ -63,6 +63,7 @@ public class HashDbMgmtPanel extends javax.swing.JPanel { /** Creates new form HashDbMgmtPanel */ private HashDbMgmtPanel() { + setName(HashDbMgmtAction.ACTION_NAME); notableTableModel = new HashSetTableModel(); initComponents(); customizeComponents(); @@ -293,9 +294,24 @@ private void addNotableButtonActionPerformed(java.awt.event.ActionEvent evt) {// JOptionPane.PLAIN_MESSAGE, null, null, derivedName); if(setName != null && !setName.equals("")) { - HashDb newDb = new HashDb(setName, Arrays.asList(new String[] {filePath}), false); - if(IndexStatus.isIngestible(newDb.status())) - newDb.setUseForIngest(true); + HashDb newDb = new HashDb(setName, Arrays.asList(new String[]{filePath}), false); + int toIndex = JOptionPane.NO_OPTION; + if (IndexStatus.isIngestible(newDb.status())) { + newDb.setUseForIngest(true); + } else { + toIndex = JOptionPane.showConfirmDialog(this, + "The database you added has no index.\n" + + "It will not be used for ingest until you create one.\n" + + "Would you like to do so now?", "No Index Exists", JOptionPane.YES_NO_OPTION); + } + + if (toIndex == JOptionPane.YES_OPTION) { + try { + newDb.createIndex(); + } catch (TskException ex) { + logger.log(Level.WARNING, "Error creating index", ex); + } + } notableTableModel.newSet(newDb); // TODO: support multiple file paths } diff --git a/Testing/build.xml b/Testing/build.xml new file mode 100644 index 0000000000..da9b6c050e --- /dev/null +++ b/Testing/build.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- You may freely edit this file. See harness/README in the NetBeans platform --> +<!-- for some information on what you could do (e.g. targets to override). --> +<!-- If you delete this file and reopen the project it will be recreated. --> +<project name="org.sleuthkit.autopsy.testing" default="netbeans" basedir="."> + <description>Builds, tests, and runs the project org.sleuthkit.autopsy.testing.</description> + <import file="nbproject/build-impl.xml"/> + + <target name="set-args"> + <property name="img_path" value="C:\Users\dfickling\Desktop\test-data\64mb2.img"/> + <property name="known_bad_path" value="C:\Users\dfickling\Desktop\test-data\notable_files.txt"/> + <property name="nsrl_path" value="C:\Users\dfickling\Desktop\NSRLComplete.txt-md5.idx"/> + <property name="keyword_path" value="C:\Users\dfickling\Desktop\test-data\notable_words.xml"/> + <property name="gold_path" value="C:\Users\dfickling\Desktop\test-data\win7-ren.txt"/> + <property name="out_path" value="C:\Users\dfickling\Desktop\test-data"/> + </target> + + <target name="check-args"> <!-- remove dependency on set-args to get from script --> + <fail message="Missing required argument: img_path" unless="img_path"/> + <fail message="Missing required argument: gold_path" unless="gold_path"/> + <fail message="Missing required argument: out_path" unless="out_path"/> + <fail message="Missing required argument: known_bad_path" unless="known_bad_path"/> + <fail message="Missing required argument: nsrl_path" unless="nsrl_path"/> + <fail message="Missing required argument: keyword_path" unless="keyword_path"/> + </target> + + <target name="regression-test" depends="check-args,init,test-init,test-build" if="exists.test.qa-functional.src.dir"> + <test test.type="qa-functional"/> + </target> + + <macrodef name="test"> + <attribute name="test.type"/> + <attribute name="disable.apple.ui" default="false"/> + <sequential> + <property name="test.config" value="default"/> + <property name="test.config.default.includes" value="**/*Test.class"/> + <property name="test.config.${test.config}.includes" value="NOTHING"/> + <metaproperty name="test.includes" value="test.config.${test.config}.includes"/> + <property name="test.config.${test.config}.excludes" value=""/> + <metaproperty name="test.excludes" value="test.config.${test.config}.excludes"/> + <mkdir dir="${build.test.@{test.type}.results.dir}"/> + <junit fork="true" failureproperty="tests.failed" errorproperty="tests.failed" filtertrace="${test.filter.trace}" tempdir="${build.test.@{test.type}.results.dir}"> + <batchtest todir="${build.test.@{test.type}.results.dir}"> + <fileset dir="${build.test.@{test.type}.classes.dir}" includes="${test.includes}" excludes="${test.excludes}"/> + </batchtest> + <classpath refid="test.@{test.type}.run.cp"/> + <syspropertyset refid="test.@{test.type}.properties"/> + <jvmarg line="${test.bootclasspath.prepend.args}"/> + <jvmarg line="${test.run.args}"/> + <sysproperty key="img_path" value="${img_path}"/> + <sysproperty key="gold_path" value="${gold_path}"/> + <sysproperty key="out_path" value="${out_path}"/> + <sysproperty key="known_bad_path" value="${known_bad_path}"/> + <sysproperty key="nsrl_path" value="${nsrl_path}"/> + <sysproperty key="keyword_path" value="${keyword_path}"/> + <!--needed to have tests NOT to steal focus when running, works in latest apple jdk update only.--> + <sysproperty key="apple.awt.UIElement" value="@{disable.apple.ui}"/> + <formatter type="brief" usefile="false"/> + <formatter type="xml"/> + </junit> + <fail message="Some tests failed; see details above."> + <condition> + <and> + <isset property="tests.failed"/> + <isfalse value="${continue.after.failing.tests}"/> + </and> + </condition> + </fail> + </sequential> + </macrodef> +</project> diff --git a/Testing/manifest.mf b/Testing/manifest.mf new file mode 100644 index 0000000000..a4acafe8e5 --- /dev/null +++ b/Testing/manifest.mf @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +OpenIDE-Module: org.sleuthkit.autopsy.testing +OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/testing/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/Testing/nbproject/build-impl.xml b/Testing/nbproject/build-impl.xml new file mode 100644 index 0000000000..50047b6352 --- /dev/null +++ b/Testing/nbproject/build-impl.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +*** GENERATED FROM project.xml - DO NOT EDIT *** +*** EDIT ../build.xml INSTEAD *** +--> +<project name="org.sleuthkit.autopsy.testing-impl" basedir=".."> + <fail message="Please build using Ant 1.7.1 or higher."> + <condition> + <not> + <antversion atleast="1.7.1"/> + </not> + </condition> + </fail> + <property file="nbproject/private/suite-private.properties"/> + <property file="nbproject/suite.properties"/> + <fail unless="suite.dir">You must set 'suite.dir' to point to your containing module suite</fail> + <property file="${suite.dir}/nbproject/private/platform-private.properties"/> + <property file="${suite.dir}/nbproject/platform.properties"/> + <macrodef name="property" uri="http://www.netbeans.org/ns/nb-module-project/2"> + <attribute name="name"/> + <attribute name="value"/> + <sequential> + <property name="@{name}" value="${@{value}}"/> + </sequential> + </macrodef> + <macrodef name="evalprops" uri="http://www.netbeans.org/ns/nb-module-project/2"> + <attribute name="property"/> + <attribute name="value"/> + <sequential> + <property name="@{property}" value="@{value}"/> + </sequential> + </macrodef> + <property file="${user.properties.file}"/> + <nbmproject2:property name="harness.dir" value="nbplatform.${nbplatform.active}.harness.dir" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/> + <nbmproject2:property name="nbplatform.active.dir" value="nbplatform.${nbplatform.active}.netbeans.dest.dir" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/> + <nbmproject2:evalprops property="cluster.path.evaluated" value="${cluster.path}" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/> + <fail message="Path to 'platform' cluster missing in $${cluster.path} property or using corrupt Netbeans Platform (missing harness)."> + <condition> + <not> + <contains string="${cluster.path.evaluated}" substring="platform"/> + </not> + </condition> + </fail> + <import file="${harness.dir}/build.xml"/> +</project> diff --git a/Testing/nbproject/genfiles.properties b/Testing/nbproject/genfiles.properties new file mode 100644 index 0000000000..b4f5cac4ea --- /dev/null +++ b/Testing/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=7c2c586b +build.xml.script.CRC32=323ed73c +build.xml.stylesheet.CRC32=a56c6a5b@1.46.2 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=64fc7023 +nbproject/build-impl.xml.script.CRC32=d8a1ea41 +nbproject/build-impl.xml.stylesheet.CRC32=238281d1@1.46.2 diff --git a/Testing/nbproject/project.properties b/Testing/nbproject/project.properties new file mode 100644 index 0000000000..17255bac6b --- /dev/null +++ b/Testing/nbproject/project.properties @@ -0,0 +1,2 @@ +javac.source=1.6 +javac.compilerargs=-Xlint -Xlint:-serial diff --git a/Testing/nbproject/project.xml b/Testing/nbproject/project.xml new file mode 100644 index 0000000000..8511bfbed8 --- /dev/null +++ b/Testing/nbproject/project.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://www.netbeans.org/ns/project/1"> + <type>org.netbeans.modules.apisupport.project</type> + <configuration> + <data xmlns="http://www.netbeans.org/ns/nb-module-project/3"> + <code-name-base>org.sleuthkit.autopsy.testing</code-name-base> + <suite-component/> + <module-dependencies> + <dependency> + <code-name-base>org.sleuthkit.autopsy.ingest</code-name-base> + <build-prerequisite/> + <compile-dependency/> + <run-dependency> + <release-version>0-1</release-version> + <specification-version>1.0</specification-version> + </run-dependency> + </dependency> + </module-dependencies> + <test-dependencies> + <test-type> + <name>qa-functional</name> + <test-dependency> + <code-name-base>org.netbeans.libs.junit4</code-name-base> + <compile-dependency/> + </test-dependency> + <test-dependency> + <code-name-base>org.netbeans.modules.jellytools.platform</code-name-base> + <compile-dependency/> + </test-dependency> + <test-dependency> + <code-name-base>org.netbeans.modules.jemmy</code-name-base> + <compile-dependency/> + </test-dependency> + <test-dependency> + <code-name-base>org.netbeans.modules.nbjunit</code-name-base> + <recursive/> + <compile-dependency/> + </test-dependency> + </test-type> + <test-type> + <name>unit</name> + <test-dependency> + <code-name-base>org.netbeans.libs.junit4</code-name-base> + <compile-dependency/> + </test-dependency> + <test-dependency> + <code-name-base>org.netbeans.modules.nbjunit</code-name-base> + <recursive/> + <compile-dependency/> + </test-dependency> + </test-type> + </test-dependencies> + <public-packages/> + </data> + </configuration> +</project> diff --git a/Testing/nbproject/suite.properties b/Testing/nbproject/suite.properties new file mode 100644 index 0000000000..29d7cc9bd6 --- /dev/null +++ b/Testing/nbproject/suite.properties @@ -0,0 +1 @@ +suite.dir=${basedir}/.. diff --git a/Testing/script/getcounts.py b/Testing/script/getcounts.py new file mode 100644 index 0000000000..8327923aca --- /dev/null +++ b/Testing/script/getcounts.py @@ -0,0 +1,38 @@ +#!/usr/bin/python +import os +import sys +import sqlite3 + +def getNumbers(inFile): + + if not inFile.endswith(".db") or not os.path.exists(inFile): + print("Not a database file: " + inFile) + return + # For now, comparing size of blackboard_artifacts, + # blackboard_attributes, + # and tsk_objects. + inFileConn = sqlite3.connect(inFile) + inFileC = inFileConn.cursor() + print(inFile) + inFileC.execute("select count(*) from tsk_objects") + inFileObjects = inFileC.fetchone()[0] + print("Objects: %d" % inFileObjects) + inFileC.execute("select count(*) from blackboard_artifacts") + inFileArtifacts = inFileC.fetchone()[0] + print("Artifacts: %d" % inFileArtifacts) + inFileC.execute("select count(*) from blackboard_attributes") + inFileAttributes = inFileC.fetchone()[0] + print("Attributes: %d" % inFileAttributes) + +def usage(): + print("This script queries the databases given as arguments for \n\ + TSK Object, Blackboard Artifact, and Blackboard Attribute counts.") + +if __name__ == "__main__": + if len(sys.argv) == 1: + usage() + argi = 1 + while argi < len(sys.argv): + getNumbers(sys.argv[argi]) + argi+=1 + diff --git a/Testing/script/regression.py b/Testing/script/regression.py new file mode 100644 index 0000000000..5277b44ac9 --- /dev/null +++ b/Testing/script/regression.py @@ -0,0 +1,247 @@ +#!/usr/bin/python +import sys +import sqlite3 +import re +import subprocess +import os.path +import shutil +import time + +# Usage: ./regression.py [-i FILE] [OPTIONS] +# Run the RegressionTest.java file, and compare the result with a gold standard +# When the -i flag is set, this script only tests the image given by FILE. +# By default, it tests every image in ./input/ +# An indexed NSRL database is expected at ./input/nsrl.txt-md5.idx, +# and an indexed notable hash database at ./input/notablehashes.txt-md5.idx +# In addition, any keywords to search for must be in ./input/notablekeywords.xml +# Options: +# -r, --rebuild Rebuild the gold standards from the test results for each image + +hadErrors = False # If any of the tests failed +results = {} # Dictionary in which to store map ({imgname}->errors) +goldDir = "gold" # Directory for gold standards (files should be ./gold/{imgname}/standard.db) +inDir = "input" # Image files, hash dbs, and keywords. +# Results will be in ./output/{datetime}/{imgname}/ +outDir = os.path.join("output",time.strftime("%Y.%m.%d-%H.%M")) + + +# Run ingest on all the images in 'input', using notablekeywords.xml and notablehashes.txt-md5.idx +def testAddImageIngest(inFile): + print "================================================" + print "Ingesting Image: " + inFile + + # Set up case directory path + testCaseName = imageName(inFile) + if os.path.exists(os.path.join(outDir,testCaseName)): + shutil.rmtree(os.path.join(outDir,testCaseName)) + os.makedirs(os.path.join(outDir,testCaseName)) + + cwd = wgetcwd() + testInFile = wabspath(inFile) + knownBadPath = os.path.join(cwd,inDir,"notablehashes.txt-md5.idx") + keywordPath = os.path.join(cwd,inDir,"notablekeywords.xml") + nsrlPath = os.path.join(cwd,inDir,"nsrl.txt-md5.idx") + + # set up ant target + args = ["ant"] + args.append("-q") + args.append("-f") + args.append(os.path.join("..","build.xml")) + args.append("regression-test") + args.append("-l") + args.append(os.path.join(cwd,outDir,testCaseName,"antlog.txt")) + args.append("-Dimg_path=" + testInFile) + args.append("-Dknown_bad_path=" + knownBadPath) + args.append("-Dkeyword_path=" + keywordPath) + args.append("-Dnsrl_path=" + nsrlPath) + args.append("-Dgold_path=" + os.path.join(cwd,goldDir)) + args.append("-Dout_path=" + os.path.join(cwd,outDir,testCaseName)) + + # print the ant testing command + print "CMD: " + " ".join(args) + + print "Starting test..." + #fnull = open(os.devnull, 'w') + #subprocess.call(args, stderr=subprocess.STDOUT, stdout=fnull) + #fnull.close(); + subprocess.call(args) + +def testCompareToGold(inFile): + print "-----------------------------------------------" + print "Comparing results for " + inFile + " with gold." + + name = imageName(inFile) + cwd = wgetcwd() + goldFile = os.path.join(cwd,goldDir,name,"standard.db") + testFile = os.path.join(cwd,outDir,name,"AutopsyTestCase","autopsy.db") + if os.path.isfile(goldFile) == False: + markError("No gold standard exists", inFile) + return + if os.path.isfile(testFile) == False: + markError("No database exists", inFile) + return + + # For now, comparing size of blackboard_artifacts, + # blackboard_attributes, + # and tsk_objects. + goldConn = sqlite3.connect(goldFile) + goldC = goldConn.cursor() + testConn = sqlite3.connect(testFile) + testC = testConn.cursor() + + print("Comparing Artifacts: ") + goldC.execute("select count(*) from blackboard_artifacts") + goldArtifacts = goldC.fetchone()[0] + testC.execute("select count(*) from blackboard_artifacts") + testArtifacts = testC.fetchone()[0] + if(goldArtifacts != testArtifacts): + errString = "Artifact counts do not match!: " + errString += str("Gold: %d, Test: %d" % (goldArtifacts, testArtifacts)) + markError(errString, inFile) + else: + print("Artifact counts match!") + print("Comparing Attributes: ") + goldC.execute("select count(*) from blackboard_attributes") + goldAttributes = goldC.fetchone()[0] + testC.execute("select count(*) from blackboard_attributes") + testAttributes = testC.fetchone()[0] + if(goldAttributes != testAttributes): + errString = "Attribute counts do not match!: " + errString += str("Gold: %d, Test: %d" % (goldAttributes, testAttributes)) + markError(errString, inFile) + else: + print("Attribute counts match!") + print("Comparing TSK Objects: ") + goldC.execute("select count(*) from tsk_objects") + goldObjects = goldC.fetchone()[0] + testC.execute("select count(*) from tsk_objects") + testObjects = testC.fetchone()[0] + if(goldObjects != testObjects): + errString = "TSK Object counts do not match!: " + errString += str("Gold: %d, Test: %d" % (goldObjects, testObjects)) + markError(errString, inFile) + else: + print("Object counts match!") + +def copyTestToGold(inFile): + print "------------------------------------------------" + print "Recreating gold standard from results." + inFile = imageName(inFile) + cwd = wgetcwd() + goldFile = os.path.join(cwd,goldDir,inFile,"standard.db") + testFile = os.path.join(cwd,outDir,inFile,"AutopsyTestCase","autopsy.db") + if os.path.exists(os.path.join(cwd,goldDir,inFile)): + shutil.rmtree(os.path.join(cwd,goldDir,inFile)) + os.makedirs(os.path.join(cwd,goldDir,inFile)) + shutil.copy(testFile, goldFile) + +class ImgType: + RAW, ENCASE, SPLIT, UNKNOWN = range(4) + +def imageType(inFile): + extStart = inFile.rfind(".") + if (extStart == -1): + return ImgType.UNKNOWN + ext = inFile[extStart:].lower() + if (ext == ".img" or ext == ".dd"): + return ImgType.RAW + elif (ext == ".e01"): + return ImgType.ENCASE + elif (ext == ".aa" or ext == ".001"): + return ImgType.SPLIT + else: + return ImgType.UNKNOWN + +def imageName(inFile): + pathEnd = inFile.rfind("/") + extStart = inFile.rfind(".") + if(extStart == -1 and extStart == -1): + return inFile + elif(extStart == -1): + return inFile[pathEnd+1:] + elif(pathEnd == -1): + return inFile[:extStart] + else: + return inFile[pathEnd+1:extStart] + +def markError(errString, inFile): + global hadErrors + hadErrors = True + errors = results.get(inFile, []) + errors.append(errString) + results[inFile] = errors + print errString + +def wgetcwd(): + proc = subprocess.Popen(("cygpath", "-m", os.getcwd()), stdout=subprocess.PIPE) + out,err = proc.communicate() + return out.rstrip() + +def wabspath(inFile): + proc = subprocess.Popen(("cygpath", "-m", os.path.abspath(inFile)), stdout=subprocess.PIPE) + out,err = proc.communicate() + return out.rstrip() + +def copyLogs(inFile): + logDir = os.path.join("..","build","test","qa-functional","work","userdir0","var","log") + shutil.copytree(logDir,os.path.join(outDir,imageName(inFile),"logs")) + +def testFile(image, rebuild): + if imageType(image) != ImgType.UNKNOWN: + testAddImageIngest(image) + #print imageName(image) + copyLogs(image) + if rebuild: + copyTestToGold(image) + else: + testCompareToGold(image) + +def usage() : + usage = "\ + Usage: ./regression.py [-i FILE] [OPTIONS] \n\n\ + Run the RegressionTest.java file, and compare the result with a gold standard \n\n\ + When the -i flag is set, this script only tests the image given by FILE.\n\ + By default, it tests every image in ./input/\n\n\ + An indexed NSRL database is expected at ./input/nsrl.txt-md5.idx,\n\ + and an indexed notable hash database at ./input/notablehashes.txt-md5.idx\n\ + In addition, any keywords to search for must be in ./input/notablekeywords.xml\n\n\ + Options:\n\n\ + -r, --rebuild\t\tRebuild the gold standards from the test results for each image" + return usage + +def main(): + rebuild = False + single = False + test = True + argi = 1 + while argi < len(sys.argv): + arg = sys.argv[argi] + if arg == "-i" and argi+1 < len(sys.argv): + single = True + argi+=1 + image = sys.argv[argi] + print "Running on single image: " + image + elif (arg == "--rebuild") or (arg == "-r"): + rebuild = True + print "Running in REBUILD mode" + else: + test = False + print usage() + argi+=1 + if single: + testFile(image, rebuild) + elif test: + for inFile in os.listdir(inDir): + testFile(os.path.join(inDir,inFile), rebuild) + + if hadErrors == True: + print "**********************************************" + print "Tests complete: There were errors" + + for k,v in results.items(): + print k + for errString in v: + print("\t%s" % errString) + +if __name__ == "__main__": + main() diff --git a/Testing/src/org/sleuthkit/autopsy/testing/Bundle.properties b/Testing/src/org/sleuthkit/autopsy/testing/Bundle.properties new file mode 100644 index 0000000000..125ec1c485 --- /dev/null +++ b/Testing/src/org/sleuthkit/autopsy/testing/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Testing diff --git a/Testing/test/qa-functional/src/org/sleuthkit/autopsy/testing/RegressionTest.java b/Testing/test/qa-functional/src/org/sleuthkit/autopsy/testing/RegressionTest.java new file mode 100644 index 0000000000..7e1708603b --- /dev/null +++ b/Testing/test/qa-functional/src/org/sleuthkit/autopsy/testing/RegressionTest.java @@ -0,0 +1,215 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011 Basis Technology Corp. + * Contact: carrier <at> sleuthkit <dot> org + * + * 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.sleuthkit.autopsy.testing; + +import java.util.logging.Logger; +import javax.swing.JDialog; +import javax.swing.JTextField; +import junit.framework.Test; +import org.netbeans.jellytools.JellyTestCase; +import org.netbeans.jellytools.NbDialogOperator; +import org.netbeans.jellytools.WizardOperator; +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.operators.JButtonOperator; +import org.netbeans.jemmy.operators.JCheckBoxOperator; +import org.netbeans.jemmy.operators.JDialogOperator; +import org.netbeans.jemmy.operators.JFileChooserOperator; +import org.netbeans.jemmy.operators.JTableOperator; +import org.netbeans.jemmy.operators.JTextFieldOperator; +import org.netbeans.junit.NbModuleSuite; +import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.ingest.IngestServiceAbstract; +/** + * This test expects the following system properties to be set: + * img_path: The fully qualified path to the image file (if split, the first file) + * out_path: The location where the case will be stored + * nsrl_path: Path to the nsrl database + * known_bad_path: Path to a database of known bad hashes + * keyword_path: Path to a keyword list xml file + * + * Without these properties set, the test will fail to run correctly. + * To run this test correctly, you should use the script 'regression.py' + * located in the 'script' directory of the Testing module. + */ +public class RegressionTest extends JellyTestCase{ + + private static final Logger logger = Logger.getLogger(RegressionTest.class.getName()); + + /** Constructor required by JUnit */ + public RegressionTest(String name) { + super(name); + } + + + /** Creates suite from particular test cases. */ + public static Test suite() { + + // run tests with specific configuration + NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(RegressionTest.class). + clusters(".*"). + enableModules(".*"); + conf = conf.addTest("testNewCaseWizardOpen", + "testNewCaseWizard", + "testAddImageWizard1", + "testConfigureIngest1", + "testConfigureHash", + "testConfigureIngest2", + "testConfigureSearch", + "testIngest"); + return NbModuleSuite.create(conf); + } + + /** Method called before each test case. */ + @Override + public void setUp() { + logger.info("######## " + System.getProperty("img_path") + " #######"); + } + + /** Method called after each test case. */ + @Override + public void tearDown() { + } + + public void testNewCaseWizardOpen() { + logger.info("New Case"); + NbDialogOperator nbdo = new NbDialogOperator("Welcome"); + JButtonOperator jbo = new JButtonOperator(nbdo, 0); // the "New Case" button + jbo.clickMouse(); + } + + public void testNewCaseWizard() { + logger.info("New Case Wizard"); + WizardOperator wo = new WizardOperator("New Case Information"); + JTextFieldOperator jtfo1 = new JTextFieldOperator(wo, 1); + jtfo1.typeText("AutopsyTestCase"); // Name the case "AutopsyTestCase" + JTextFieldOperator jtfo0 = new JTextFieldOperator(wo, 0); + jtfo0.typeText(System.getProperty("out_path")); + wo.btNext().clickMouse(); + JTextFieldOperator jtfo2 = new JTextFieldOperator(wo, 0); + jtfo2.typeText("000"); // Set the case number + JTextFieldOperator jtfo3 = new JTextFieldOperator(wo, 1); + jtfo3.typeText("Examiner 1"); // Set the case examiner + wo.btFinish().clickMouse(); + } + + public void testAddImageWizard1() { + logger.info("AddImageWizard 1"); + WizardOperator wo = new WizardOperator("Add Image"); + JTextFieldOperator jtfo0 = new JTextFieldOperator(wo, 0); + String imageDir = System.getProperty("img_path"); + ((JTextField)jtfo0.getSource()).setText(imageDir); + wo.btNext().clickMouse(); + long start = System.currentTimeMillis(); + while(!wo.btNext().isEnabled()) { + new Timeout("pausing", 1000).sleep(); // give it a second (or five) to process + } + logger.info("Add image took " + (System.currentTimeMillis()-start) + "ms"); + wo.btNext().clickMouse(); + } + + public void testConfigureIngest1() { + logger.info("Ingest 1"); + WizardOperator wo = new WizardOperator("Add Image"); + JTableOperator jto = new JTableOperator(wo, 0); + int row = jto.findCellRow("Hash Lookup", 1, 0); + jto.clickOnCell(row, 1); + JButtonOperator jbo1 = new JButtonOperator(wo, "Advanced"); + jbo1.pushNoBlock(); + } + + public void testConfigureHash() { + logger.info("Hash Configure"); + JDialog jd = JDialogOperator.waitJDialog("Hash Database Configuration", false, false); + JDialogOperator jdo = new JDialogOperator(jd); + String databaseDir = System.getProperty("nsrl_path"); + String badDir = System.getProperty("known_bad_path"); + JButtonOperator jbo0 = new JButtonOperator(jdo, "Change"); + jbo0.pushNoBlock(); + JFileChooserOperator jfco0 = new JFileChooserOperator(); + jfco0.chooseFile(databaseDir); + JButtonOperator jbo1 = new JButtonOperator(jdo, "Add Notable Database"); + jbo1.pushNoBlock(); + JFileChooserOperator jfco1 = new JFileChooserOperator(); + jfco1.chooseFile(badDir); + JDialog jd2 = JDialogOperator.waitJDialog("New Hash Set", false, false); + JDialogOperator jdo2 = new JDialogOperator(jd2); + JButtonOperator jbo2 = new JButtonOperator(jdo2, "OK", 0); + jbo2.pushNoBlock(); + // Used if the database has no index + //JDialog jd3 = JDialogOperator.waitJDialog("No Index Exists", false, false); + //JDialogOperator jdo3 = new JDialogOperator(jd3); + //JButtonOperator jbo3 = new JButtonOperator(jdo3, "Yes", 0); + //new Timeout("pausing", 1000).sleep(); // give it a second (or five) to process + //jbo3.pushNoBlock(); + JButtonOperator jbo4 = new JButtonOperator(jdo, "OK", 0); + jbo4.pushNoBlock(); + } + + public void testConfigureIngest2() { + logger.info("Ingest 2"); + WizardOperator wo = new WizardOperator("Add Image"); + JTableOperator jto = new JTableOperator(wo, 0); + int row = jto.findCellRow("Keyword Search", 1, 0); + jto.clickOnCell(row, 1); + JButtonOperator jbo1 = new JButtonOperator(wo, "Advanced"); + jbo1.pushNoBlock(); + } + + public void testConfigureSearch() { + logger.info("Search Configure"); + JDialog jd = JDialogOperator.waitJDialog("Keyword List Configuration", false, false); + JDialogOperator jdo = new JDialogOperator(jd); + String words = System.getProperty("keyword_path"); + JButtonOperator jbo0 = new JButtonOperator(jdo, "Import List", 0); + jbo0.pushNoBlock(); + JFileChooserOperator jfco0 = new JFileChooserOperator(); + jfco0.chooseFile(words); + JCheckBoxOperator jcbo = new JCheckBoxOperator(jdo, "Use during triage / ingest", 0); + jcbo.doClick(); + JButtonOperator jbo2 = new JButtonOperator(jdo, "OK", 0); + jbo2.pushNoBlock(); + WizardOperator wo = new WizardOperator("Add Image"); + wo.btNext().clickMouse(); + wo.btFinish().clickMouse(); + } + + public void testIngest() { + logger.info("Ingest 3"); + long start = System.currentTimeMillis(); + IngestManager man = IngestManager.getDefault(); + while(man.isEnqueueRunning()) { + new Timeout("pausing", 5000).sleep(); // give it a second (or five) to process + } + logger.info("Enqueue took " + (System.currentTimeMillis()-start) + "ms"); + while(man.isIngestRunning()) { + new Timeout("pausing", 1000).sleep(); // give it a second (or five) to process + } + new Timeout("pausing", 15000).sleep(); // give it a second (or fifteen) to process + boolean sleep = true; + while (sleep) { + new Timeout("pausing", 5000).sleep(); // give it a second (or five) to process + sleep = false; + for (IngestServiceAbstract serv : IngestManager.enumerateFsContentServices()) { + sleep = sleep || serv.hasBackgroundJobsRunning(); + } + } + logger.info("Ingest (including enqueue) took " + (System.currentTimeMillis()-start) + "ms"); + new Timeout("pausing", 5000).sleep(); // allow keyword search to finish saving artifacts, just in case + } +} \ No newline at end of file diff --git a/nbproject/platform.properties b/nbproject/platform.properties index 38ecd5a92e..95295edd72 100644 --- a/nbproject/platform.properties +++ b/nbproject/platform.properties @@ -1,4 +1,5 @@ cluster.path=\ + ${nbplatform.active.dir}/harness:\ ${nbplatform.active.dir}/java:\ ${nbplatform.active.dir}/platform disabled.modules=\ diff --git a/nbproject/project.properties b/nbproject/project.properties index 103f5f1935..e499096b46 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -29,7 +29,8 @@ modules=\ ${project.org.sleuthkit.autopsy.hashdatabase}:\ ${project.org.gnu.trove}:\ ${project.org.sleuthkit.autopsy.recentactivity}:\ - ${project.org.sleuthkit.autopsy.report} + ${project.org.sleuthkit.autopsy.report}:\ + ${project.org.sleuthkit.autopsy.testing} project.org.gnu.trove=trove project.org.sleuthkit.autopsy.casemodule=Case project.org.sleuthkit.autopsy.corecomponentinterfaces=CoreComponentInterfaces @@ -43,4 +44,5 @@ project.org.sleuthkit.autopsy.keywordsearch=KeywordSearch project.org.sleuthkit.autopsy.menuactions=MenuActions project.org.sleuthkit.autopsy.datamodel=DataModel project.org.sleuthkit.autopsy.recentactivity=RecentActivity -project.org.sleuthkit.autopsy.report=Report \ No newline at end of file +project.org.sleuthkit.autopsy.report=Report +project.org.sleuthkit.autopsy.testing=Testing -- GitLab