diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/datasourcesummary/UserActivitySummaryTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/datasourcesummary/UserActivitySummaryTests.java index ac349c7b5aa889d9336480856aaecf9e889f5cea..1ed9dbccafbc07b83562ce5ac832a0db9356bf73 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/datasourcesummary/UserActivitySummaryTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/datasourcesummary/UserActivitySummaryTests.java @@ -32,13 +32,13 @@ import org.sleuthkit.datamodel.TskCoreException; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.integrationtesting.IntegrationTest; -import org.sleuthkit.autopsy.integrationtesting.IntegrationTests; +import org.sleuthkit.autopsy.integrationtesting.IntegrationTestGroup; /** * Tests for the UserActivitySummary class. */ -@ServiceProvider(service = IntegrationTests.class) -public class UserActivitySummaryTests implements IntegrationTests { +@ServiceProvider(service = IntegrationTestGroup.class) +public class UserActivitySummaryTests implements IntegrationTestGroup { /** * Runs UserActivitySummary.getRecentDomains for all data sources found in diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/ConfigurationModule.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/ConfigurationModule.java index 9e45cd858e4772647eac93778a34b6ca0b6f7145..6df583b1dceaebcb438d9baa3cd732b23749a9a3 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/ConfigurationModule.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/ConfigurationModule.java @@ -18,17 +18,33 @@ */ package org.sleuthkit.autopsy.integrationtesting; -import java.util.Map; import org.sleuthkit.autopsy.ingest.IngestJobSettings; /** - * - * @author gregd + * Interface for a module that performs configuration. Implementers of this + * class must have a no-parameter constructor in order to be instantiated + * properly. */ public interface ConfigurationModule<T> { + /** + * Configures the autopsy environment and updates the current ingest job + * settings to augment with any additional templates that may need to be + * added or any other ingest job setting changes. + * + * @param curSettings The current IngestJobSettings. + * @param parameters The parameters object for this configuration module. + * @return The new IngestJobSettings or 'curSettings' if no + * IngestJobSettings need to be made. + */ IngestJobSettings configure(IngestJobSettings curSettings, T parameters); + /** + * In the event that settings outside the IngestJobSettings were altered, + * this provides a means of reverting those changes when the test completes. + * Configuration changes in the 'configure' method can be captured in this + * object for revert to utilize. + */ default void revert() { } } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/ConfigurationModuleManager.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/ConfigurationModuleManager.java index 241d13f4f36cde0c6f82bff43e9be3ca52a9118a..08ec03aaa02882acedf1291f2dc1ed967eef05a1 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/ConfigurationModuleManager.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/ConfigurationModuleManager.java @@ -1,7 +1,20 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2020 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.integrationtesting; @@ -23,20 +36,25 @@ import org.sleuthkit.autopsy.ingest.IngestModuleTemplate; import org.sleuthkit.autopsy.integrationtesting.config.ConfigDeserializer; import org.sleuthkit.autopsy.integrationtesting.config.ParameterizedResourceConfig; -import org.sleuthkit.autopsy.integrationtesting.config.TestSuiteConfig; /** - * - * @author gregd + * In charge of running configuration modules to set up the environment and then + * running revert when the test completes. */ public class ConfigurationModuleManager { + private static final IngestModuleFactoryService ingestModuleFactories = new IngestModuleFactoryService(); private static final Logger logger = Logger.getLogger(ConfigurationModuleManager.class.getName()); - + private static final IngestJobSettings.IngestType DEFAULT_INGEST_FILTER_TYPE = IngestJobSettings.IngestType.ALL_MODULES; private static final Set<String> DEFAULT_EXCLUDED_MODULES = Stream.of("Plaso").collect(Collectors.toSet()); private static final ConfigDeserializer configDeserializer = new ConfigDeserializer(); - + + /** + * Reverts the effects of the given configuration module objects. + * + * @param configModules The configuration modules. + */ void revertConfigurationModules(List<ConfigurationModule<?>> configModules) { List<ConfigurationModule<?>> reversed = new ArrayList<>(configModules); Collections.reverse(reversed); @@ -45,10 +63,25 @@ void revertConfigurationModules(List<ConfigurationModule<?>> configModules) { } } + /** + * Returns a profile name to be used with IngestJobSettings for a given + * caseName. + * + * @param caseName The case name. + * @return The name of the profile. + */ static String getProfileName(String caseName) { return String.format("integrationTestProfile-%s", caseName); } + /** + * Returns a default IngestJobSettings object in the event that no + * configuration modules are specified. + * + * @param caseName The name of the case used for the profile name of the + * settings. + * @return The default ingest job settings. + */ private IngestJobSettings getDefaultIngestConfig(String caseName) { return new IngestJobSettings( getProfileName(caseName), @@ -60,11 +93,23 @@ private IngestJobSettings getDefaultIngestConfig(String caseName) { ); } - Pair<IngestJobSettings, List<ConfigurationModule<?>>> runConfigurationModules(String caseName, TestSuiteConfig config) { - if (CollectionUtils.isEmpty(config.getConfigurationModules())) { + /** + * Runs configuration modules in the TestSuiteConfig. + * + * @param caseName The name of the case. + * @param configModules The configuration modules to be run specified by the + * resource and their accompanying parameters. + * @return A tuple of the generated IngestJobSettings and a list of the + * generated configuration modules to later be used for reverting any + * environmental changes. + */ + Pair<IngestJobSettings, List<ConfigurationModule<?>>> runConfigurationModules(String caseName, List<ParameterizedResourceConfig> configModules) { + // if no config modules, return default ingest settings + if (CollectionUtils.isEmpty(configModules)) { return Pair.of(getDefaultIngestConfig(caseName), Collections.emptyList()); } + // create a base ingest job settings object with no templates. IngestJobSettings curConfig = new IngestJobSettings( getProfileName(caseName), DEFAULT_INGEST_FILTER_TYPE, @@ -72,8 +117,10 @@ Pair<IngestJobSettings, List<ConfigurationModule<?>>> runConfigurationModules(St List<ConfigurationModule<?>> configurationModuleCache = new ArrayList<>(); - for (ParameterizedResourceConfig configModule : config.getConfigurationModules()) { + // run through the configuration for each configuration module + for (ParameterizedResourceConfig configModule : configModules) { Pair<IngestJobSettings, ConfigurationModule<?>> ingestResult = runConfigurationModule(curConfig, configModule); + // if there are results, update to the new ingest job settings and cache the config module. if (ingestResult != null) { curConfig = ingestResult.first() == null ? curConfig : ingestResult.first(); if (ingestResult.second() != null) { @@ -84,7 +131,19 @@ Pair<IngestJobSettings, List<ConfigurationModule<?>>> runConfigurationModules(St return Pair.of(curConfig, configurationModuleCache); } + /** + * Run a configuration module as specified by the paramerized resource + * config returning the acquired configuration module and the updated ingest + * job settings. + * + * @param curConfig The current ingest job settings. + * @param configModule The resource identifying the config module and any + * accompanying parameters. + * @return A tuple containing the ingest job settings and the instantiated + * configuration module that generated changes (used later for reverting). + */ private Pair<IngestJobSettings, ConfigurationModule<?>> runConfigurationModule(IngestJobSettings curConfig, ParameterizedResourceConfig configModule) { + // acquire class described by resource Class<?> clazz = null; try { clazz = Class.forName(configModule.getResource()); @@ -93,11 +152,13 @@ private Pair<IngestJobSettings, ConfigurationModule<?>> runConfigurationModule(I return null; } + // assure that the class is a configuration module. if (clazz == null || !ConfigurationModule.class.isAssignableFrom(clazz)) { logger.log(Level.WARNING, String.format("%s does not seem to be an instance of a configuration module.", configModule.getResource())); return null; } + // determine generic parameter type Type configurationModuleType = Stream.of(clazz.getGenericInterfaces()) .filter(type -> type instanceof ParameterizedType && ((ParameterizedType) type).getRawType().equals(ConfigurationModule.class)) .map(type -> ((ParameterizedType) type).getActualTypeArguments()[0]) @@ -109,6 +170,7 @@ private Pair<IngestJobSettings, ConfigurationModule<?>> runConfigurationModule(I return null; } + // instantiate the object from the class and run the configure method. ConfigurationModule<?> configModuleObj = null; Object result = null; try { @@ -119,10 +181,11 @@ private Pair<IngestJobSettings, ConfigurationModule<?>> runConfigurationModule(I logger.log(Level.SEVERE, String.format("There was an error calling configure method on Configuration Module %s", configModule.getResource()), ex); } + // return results or an error if no results returned. if (result instanceof IngestJobSettings) { return Pair.of((IngestJobSettings) result, configModuleObj); } else { - logger.log(Level.SEVERE, String.format("Could not retrieve IngestJobSettings from %s", configModule.getResource())); + logger.log(Level.SEVERE, String.format("Could not retrieve IngestJobSettings or null was returned from %s", configModule.getResource())); return null; } } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/IngestModuleFactoryService.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/IngestModuleFactoryService.java index 1e4b8aefec0240149a4b535768af623359b3445f..467bb5e65cdd3c1c286e2e1b199faa44977bdaa4 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/IngestModuleFactoryService.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/IngestModuleFactoryService.java @@ -1,21 +1,59 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2020 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.integrationtesting; import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.openide.util.Lookup; import org.sleuthkit.autopsy.ingest.IngestModuleFactory; +import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter; +import org.sleuthkit.autopsy.python.JythonModuleLoader; /** - * - * @author gregd + * Class for obtaining ingest module factories since IngestModuleFactoryLoader + * is not accessible. */ public class IngestModuleFactoryService { + + /** + * Returns any found ingest modules from implementers of + * IngestModuleFactory, IngestModuleFactoryAdapter, and Jython modules. + * + * @return The ingest module factories. + */ public List<IngestModuleFactory> getFactories() { - return new ArrayList<>(Lookup.getDefault().lookupAll(IngestModuleFactory.class)); + // factories using lookup for IngestModuleFactory, IngestModuleFactoryAdapter and jython modules + Stream<Collection<? extends IngestModuleFactory>> factoryCollections = Stream.of( + Lookup.getDefault().lookupAll(IngestModuleFactory.class), + Lookup.getDefault().lookupAll(IngestModuleFactoryAdapter.class), + JythonModuleLoader.getIngestModuleFactories()); + + // get unique by module display name + Map<String, IngestModuleFactory> factories = factoryCollections + .flatMap(coll -> coll.stream()) + .collect(Collectors.toMap(f -> f.getModuleDisplayName(), f -> f, (f1, f2) -> f1)); + + // return list of values + return new ArrayList<>(factories.values()); } } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/IngestModuleSetupManager.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/IngestModuleSetupManager.java index 47b9354bbc36083741d8d073207530f5a9bee781..e25c7a2edb37c659077c0ec573a9ed8139ae4fae 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/IngestModuleSetupManager.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/IngestModuleSetupManager.java @@ -33,48 +33,68 @@ */ public class IngestModuleSetupManager implements ConfigurationModule<IngestModuleSetupManager.ConfigArgs> { + /** + * The parameters used when calling 'configure' in this class. + */ public static class ConfigArgs { + private final List<String> modules; + /** + * Main constructor. + * + * @param modules The ingest module factories to be loaded. + */ public ConfigArgs(List<String> modules) { this.modules = modules; } + /** + * @return The ingest module factories to be loaded. + */ public List<String> getModules() { return modules; } } - + private static final IngestModuleFactoryService ingestModuleFactories = new IngestModuleFactoryService(); - - + @Override public IngestJobSettings configure(IngestJobSettings curSettings, IngestModuleSetupManager.ConfigArgs parameters) { - + + // get the profile from the IngestJobSettings String context = curSettings.getExecutionContext(); if (StringUtils.isNotBlank(context) && context.indexOf('.') > 0) { context = context.substring(0, context.indexOf('.')); } - + + // get current templates Map<String, IngestModuleTemplate> curTemplates = curSettings.getEnabledIngestModuleTemplates().stream() .collect(Collectors.toMap(t -> t.getModuleFactory().getClass().getCanonicalName(), t -> t, (t1, t2) -> t1)); - + + // get all the factories determined by canonical name Map<String, IngestModuleFactory> allFactories = ingestModuleFactories.getFactories().stream() .collect(Collectors.toMap(f -> f.getClass().getCanonicalName(), f -> f, (f1, f2) -> f1)); - + + // add current templates to the list of templates to return. List<IngestModuleTemplate> newTemplates = new ArrayList<>(curTemplates.values()); - + + // if there are parameters, add any relevant ingest module factories if (parameters != null && !CollectionUtils.isEmpty(parameters.getModules())) { List<IngestModuleTemplate> templatesToAdd = parameters.getModules().stream() - .filter((className) -> !curTemplates.containsKey(className)) - .map((className) -> allFactories.get(className)) + // ensure only one of each type of factory is added + .distinct() + // if the factory to be added is already contained in curTemplates or allFactories does not contain item, null is returned + .map((className) -> curTemplates.containsKey(className) ? null : allFactories.get(className)) + // filter out any null items .filter((factory) -> factory != null) + // create a template for any remaining items .map((factory) -> new IngestModuleTemplate(factory, factory.getDefaultIngestJobSettings())) .collect(Collectors.toList()); - + newTemplates.addAll(templatesToAdd); } - + return new IngestJobSettings( context, IngestJobSettings.IngestType.ALL_MODULES, diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/IntegrationTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/IntegrationTestGroup.java similarity index 81% rename from Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/IntegrationTests.java rename to Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/IntegrationTestGroup.java index 59aa5ae5cd4352bf0e252f45edfe40f680f98400..244f7b35f967eda765452d162a4998a093340a3e 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/IntegrationTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/IntegrationTestGroup.java @@ -19,27 +19,31 @@ package org.sleuthkit.autopsy.integrationtesting; /** - * Basic interface for integration test suite. + * Basic interface for integration test group. */ -public interface IntegrationTests { +public interface IntegrationTestGroup { + /** * Allows for a test to perform any necessary setup. */ - default void setup() {} - + default void setup() { + } + /** * Allows for a test to perform any necessary disposing of resources. */ - default void tearDown() {} - + default void tearDown() { + } + /** * Allows for this suite of tests to perform any necessary setup. */ - default void setupClass() {} - + default void setupClass() { + } + /** * Allows for this suite of tests to dispose of resources. */ - default void tearDownClass() {} + default void tearDownClass() { + } } - diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/MainTestRunner.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/MainTestRunner.java index a78f211bf5b7d62c830ecd6226a40ccc2e31336e..13d351c1b6390958188c06e2717d43b35f462b32 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/MainTestRunner.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/MainTestRunner.java @@ -54,7 +54,6 @@ import org.sleuthkit.autopsy.integrationtesting.config.TestingConfig; import org.sleuthkit.autopsy.testutils.IngestUtils; import org.sleuthkit.datamodel.TskCoreException; -import static ucar.unidata.util.Format.i; /** * Main entry point for running integration tests. Handles processing @@ -94,7 +93,7 @@ public void runIntegrationTests() { String configFile = System.getProperty(CONFIG_FILE_KEY); IntegrationTestConfig config; try { - config = configDeserializer.getConfigFromFile(configFile); + config = configDeserializer.getConfigFromFile(new File(configFile)); } catch (IOException ex) { logger.log(Level.WARNING, "There was an error processing integration test config at " + configFile, ex); return; @@ -113,7 +112,12 @@ public void runIntegrationTests() { for (CaseType caseType : IntegrationCaseType.getCaseTypes(testSuiteConfig.getCaseTypes())) { // create an autopsy case for each case in the config and for each case type for the specified case. // then run ingest for the case. - Case autopsyCase = createCaseWithDataSources(envConfig, caseName, caseType, testSuiteConfig.getDataSources()); + Case autopsyCase = createCaseWithDataSources( + envConfig.getWorkingDirectory(), + envConfig.getRootCaseOutputPath(), + caseName, + caseType, + testSuiteConfig.getDataSources()); if (autopsyCase == null || autopsyCase != Case.getCurrentCase()) { logger.log(Level.WARNING, @@ -122,24 +126,29 @@ public void runIntegrationTests() { return; } + // run configuration modules and get result Pair<IngestJobSettings, List<ConfigurationModule<?>>> configurationResult - = configurationModuleManager.runConfigurationModules(caseName, testSuiteConfig); + = configurationModuleManager.runConfigurationModules(caseName, testSuiteConfig.getConfigurationModules()); IngestJobSettings ingestSettings = configurationResult.first(); List<ConfigurationModule<?>> configModules = configurationResult.second(); + // run ingest with ingest settings derived from configuration modules. runIngest(autopsyCase, ingestSettings, caseName); // once ingested, run integration tests to produce output. OutputResults results = runIntegrationTests(testSuiteConfig.getIntegrationTests()); + // revert any autopsy environment changes made by configuration modules. configurationModuleManager.revertConfigurationModules(configModules); String outputFolder = PathUtil.getAbsolutePath(envConfig.getWorkingDirectory(), envConfig.getRootTestOutputPath()); - + // write the results for the case to a file results.serializeToFile( - Paths.get(outputFolder, testSuiteConfig.getRelativeOutputPath()).toString(), + envConfig.getUseRelativeOutput() == true ? + Paths.get(outputFolder, testSuiteConfig.getRelativeOutputPath()).toString() : + outputFolder, testSuiteConfig.getName(), caseType ); @@ -155,10 +164,22 @@ public void runIntegrationTests() { } } - private Case createCaseWithDataSources(EnvConfig envConfig, String caseName, CaseType caseType, List<String> dataSourcePaths) { + /** + * Creates a case with the given data sources. + * + * @param workingDirectory The base working directory (if caseOutputPath or + * dataSourcePaths are relative, this is the working directory). + * @param caseOutputPath The case output path. + * @param caseName The name of the case (unique case name appends time + * stamp). + * @param caseType The type of case (single user / multi user). + * @param dataSourcePaths The path to the data sources. + * @return The generated case that is now the current case. + */ + private Case createCaseWithDataSources(String workingDirectory, String caseOutputPath, String caseName, CaseType caseType, List<String> dataSourcePaths) { Case openCase = null; String uniqueCaseName = String.format("%s_%s", caseName, TimeStampUtils.createTimeStamp()); - String outputFolder = PathUtil.getAbsolutePath(envConfig.getWorkingDirectory(), envConfig.getRootCaseOutputPath()); + String outputFolder = PathUtil.getAbsolutePath(workingDirectory, caseOutputPath); String caseOutputFolder = Paths.get(outputFolder, uniqueCaseName).toString(); File caseOutputFolderFile = new File(caseOutputFolder); if (!caseOutputFolderFile.exists()) { @@ -178,7 +199,7 @@ private Case createCaseWithDataSources(EnvConfig envConfig, String caseName, Cas } } break; - + case MULTI_USER_CASE: // TODO default: @@ -190,7 +211,7 @@ private Case createCaseWithDataSources(EnvConfig envConfig, String caseName, Cas return null; } - addDataSourcesToCase(PathUtil.getAbsolutePaths(envConfig.getWorkingDirectory(), dataSourcePaths), caseName); + addDataSourcesToCase(PathUtil.getAbsolutePaths(workingDirectory, dataSourcePaths), caseName); return openCase; } @@ -198,7 +219,7 @@ private Case createCaseWithDataSources(EnvConfig envConfig, String caseName, Cas * Adds the data sources to the current case. * * @param pathStrings The list of paths for the data sources. - * @param caseName The name of the case. + * @param caseName The name of the case (used for error messages). */ private void addDataSourcesToCase(List<String> pathStrings, String caseName) { for (String strPath : pathStrings) { @@ -218,6 +239,13 @@ private void addDataSourcesToCase(List<String> pathStrings, String caseName) { } } + /** + * Runs ingest on the current case. + * @param openCase The currently open case. + * @param ingestJobSettings The ingest job settings to be used. + * @param caseName The name of the case to be used for error messages. + * @return The case that was ingested. + */ private Case runIngest(Case openCase, IngestJobSettings ingestJobSettings, String caseName) { try { // IngestJobSettings ingestJobSettings = SETUP_UTIL.setupEnvironment(envConfig, testSuiteConfig); @@ -232,16 +260,14 @@ private Case runIngest(Case openCase, IngestJobSettings ingestJobSettings, Strin /** * Runs the integration tests and serializes results to disk. * - * @param envConfig The overall config. * @param testSuiteConfig The configuration for a particular case. - * @param caseType The case type (single user / multi user) to create. */ private OutputResults runIntegrationTests(TestingConfig testSuiteConfig) { // this will capture output results OutputResults results = new OutputResults(); - // run through each ConsumerIntegrationTest - for (IntegrationTests testGroup : Lookup.getDefault().lookupAll(IntegrationTests.class)) { + // run through each IntegrationTestGroup + for (IntegrationTestGroup testGroup : Lookup.getDefault().lookupAll(IntegrationTestGroup.class)) { // if test should not be included in results, skip it. if (!testSuiteConfig.hasIncludedTest(testGroup.getClass().getCanonicalName())) { @@ -261,11 +287,13 @@ private OutputResults runIntegrationTests(TestingConfig testSuiteConfig) { for (Method testMethod : testMethods) { Object[] parameters = new Object[0]; + // no more than 1 parameter in a test method. if (testMethod.getParameters().length > 1) { throw new IllegalArgumentException(String.format("Could not call method %s in class %s. Multiple parameters cannot be handled.", testMethod.getName(), testGroup.getClass().getCanonicalName())); + // if there is a parameter, deserialize parameters to the specified type. } else if (testMethod.getParameters().length > 0) { - parameters = new Object[]{configDeserializer.convertToObj(parametersMap, testMethod.getParameters().getClass())}; + parameters = new Object[]{configDeserializer.convertToObj(parametersMap, testMethod.getParameterTypes()[0])}; } Object serializableResult = runIntegrationTestMethod(testGroup, testMethod, parameters); @@ -284,20 +312,22 @@ private OutputResults runIntegrationTests(TestingConfig testSuiteConfig) { return results; } + /** * Runs a test method in a test suite on the current case. - * * @param testGroup The test suite to which the method belongs. * @param testMethod The java reflection method to run. + * @param parameters The parameters to use with this method or none/empty array. + * @return The results of running the method. */ - private Object runIntegrationTestMethod(IntegrationTests testGroup, Method testMethod, Object[] parameters) { + private Object runIntegrationTestMethod(IntegrationTestGroup testGroup, Method testMethod, Object[] parameters) { testGroup.setup(); // run the test method and get the results Object serializableResult = null; try { - serializableResult = testMethod.invoke(testGroup, parameters); + serializableResult = testMethod.invoke(testGroup, parameters == null ? new Object[0] : parameters); } catch (IllegalAccessException | IllegalArgumentException ex) { logger.log(Level.WARNING, String.format("test method %s in %s could not be properly invoked", diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/OutputResults.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/OutputResults.java index fc65a1ef5a7c9e9aeed5591d0fc067103a8b8fc9..4f21dd6d24680c693bac0fe6afc8f8a6c3194bf5 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/OutputResults.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/OutputResults.java @@ -185,7 +185,6 @@ protected MappingNode representJavaBean(Set<Property> properties, Object javaBea /** * Serializes results of a test to a yaml file. * - * @param results The results to serialize. * @param outputFolder The folder where the yaml should be written. * @param caseName The name of the case (used to determine filename). * @param caseType The type of case (used to determine filename). diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/PathUtil.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/PathUtil.java index 9eec6839fdf7a3cb7b6d70b0ccd24a9af0609d91..16e6ec0094119da953d3d9e54dcb2acbd0d1d75c 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/PathUtil.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/PathUtil.java @@ -28,6 +28,15 @@ */ public final class PathUtil { + /** + * If the relPath is an absolute path, that is returned. Otherwise, it is + * treated as a relative path using the working directory. + * + * @param workingDirectory The working directory. + * @param relPath The path. + * @return The relPath with the workingDirectory prepended if relPath is a + * relative path. + */ public static String getAbsolutePath(String workingDirectory, String relPath) { if (StringUtils.isBlank(workingDirectory)) { return relPath; @@ -35,16 +44,25 @@ public static String getAbsolutePath(String workingDirectory, String relPath) { if (Paths.get(relPath).isAbsolute()) { return relPath; } else { - return Paths.get(workingDirectory, relPath).toString(); + return Paths.get(workingDirectory, relPath).toString(); } } } + /** + * If one of the relPaths is an absolute path, that is returned. Otherwise, + * each is treated as a relative path using the working directory. + * + * @param workingDirectory The working directory. + * @param relPath The paths. + * @return The list of paths with the workingDirectory prepended if a path + * is a relative path. + */ public static List<String> getAbsolutePaths(String workingDirectory, List<String> relPaths) { if (relPaths == null) { return null; } - + return relPaths.stream() .map((relPath) -> getAbsolutePath(workingDirectory, relPath)) .collect(Collectors.toList()); diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/ConfigDeserializer.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/ConfigDeserializer.java index a6030f66191d7d23ca4c0dce427af05ed5fb49b2..1deda4ff7628e62f21af7d617c1f47a5afb05dde 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/ConfigDeserializer.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/ConfigDeserializer.java @@ -1,13 +1,25 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2020 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.integrationtesting.config; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.type.CollectionType; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -30,8 +42,7 @@ import org.sleuthkit.autopsy.integrationtesting.PathUtil; /** - * - * @author gregd + * Handles deserializing configuration items. */ public class ConfigDeserializer { @@ -39,6 +50,14 @@ public class ConfigDeserializer { private static final Logger logger = Logger.getLogger(ConfigDeserializer.class.getName()); private static final ObjectMapper mapper = new ObjectMapper(); + /** + * Creates an object of type T by re-deserializing the map to the specified + * type. + * + * @param toConvert The map to convert. + * @param clazz The type of object to deserialize to. + * @return The object converted to the specified type. + */ public <T> T convertToObj(Map<String, Object> toConvert, Type clazz) { GsonBuilder builder = new GsonBuilder(); Gson gson = builder.create(); @@ -48,14 +67,16 @@ public <T> T convertToObj(Map<String, Object> toConvert, Type clazz) { /** * Deserializes the json config specified at the given path into the java - * equivalent IntegrationTestConfig object. + * equivalent IntegrationTestConfig object. This uses information in env + * config file to determine test suite locations. * * @param filePath The path to the config. * @return The java object. * @throws IOException If there is an error opening the file. + * @throws IllegalStateException If the file cannot be validated. */ - public IntegrationTestConfig getConfigFromFile(String envConfigFile) throws IOException, IllegalStateException { - EnvConfig envConfig = getEnvConfig(new File(envConfigFile)); + public IntegrationTestConfig getConfigFromFile(File envConfigFile) throws IOException, IllegalStateException { + EnvConfig envConfig = getEnvConfig(envConfigFile); String testSuiteConfigPath = PathUtil.getAbsolutePath(envConfig.getWorkingDirectory(), envConfig.getRootTestSuitesPath()); return new IntegrationTestConfig( @@ -64,11 +85,28 @@ public IntegrationTestConfig getConfigFromFile(String envConfigFile) throws IOEx ); } + /** + * Deserializes the specified json file into an EnvConfig object. + * + * @param envConfigFile The file location for the environmental config. + * @return The deserialized file. + * @throws IOException + * @throws IllegalStateException If the file cannot be validated. + */ public EnvConfig getEnvConfig(File envConfigFile) throws IOException, IllegalStateException { EnvConfig config = mapper.readValue(envConfigFile, EnvConfig.class); return validate(envConfigFile, config); } + /** + * Validates the environment configuration file. + * + * @param envConfigFile The file location of the config (used for setting + * working directory if not specified). + * @param config The environmental config that was deserialized. + * @return The updated config. + * @throws IllegalStateException If could not be validated. + */ private EnvConfig validate(File envConfigFile, EnvConfig config) throws IllegalStateException { if (config == null || StringUtils.isBlank(config.getRootCaseOutputPath()) || StringUtils.isBlank(config.getRootTestOutputPath())) { throw new IllegalStateException("EnvConfig must have both the root case output path and the root test output path set."); @@ -82,6 +120,16 @@ private EnvConfig validate(File envConfigFile, EnvConfig config) throws IllegalS return config; } + /** + * Derives a list of test suite config's specified in the configFile. The + * root directory is the same or an ancestor directory of this configFile + * used for the relative output path. + * + * @param rootDirectory The ancestor directory of configFile. + * @param configFile The configFile to read. + * @return The list of test suite configs found after invalid configs are + * filtered. + */ public List<TestSuiteConfig> getTestSuiteConfig(File rootDirectory, File configFile) { try { JsonNode root = mapper.readTree(configFile); @@ -97,13 +145,39 @@ public List<TestSuiteConfig> getTestSuiteConfig(File rootDirectory, File configF } } - public List<TestSuiteConfig> getTestSuiteConfigs(File rootDirectory) { - Collection<File> jsonFiles = FileUtils.listFiles(rootDirectory, new String[]{JSON_EXT}, true); - return jsonFiles.stream() - .flatMap((file) -> getTestSuiteConfig(rootDirectory, file).stream()) - .collect(Collectors.toList()); + /** + * Finds all test suite config json files within this directory and sub + * directories or if a file is specified, just that file is loaded. + * + * @param fileOrDirectory The parent directory of test suite configurations + * or the configuration file itself. + * @return The list of determined test suite configurations found. + */ + public List<TestSuiteConfig> getTestSuiteConfigs(File fileOrDirectory) { + if (fileOrDirectory.isDirectory()) { + Collection<File> jsonFiles = FileUtils.listFiles(fileOrDirectory, new String[]{JSON_EXT}, true); + return jsonFiles.stream() + .flatMap((file) -> getTestSuiteConfig(fileOrDirectory, file).stream()) + .collect(Collectors.toList()); + } else if (fileOrDirectory.isFile()) { + return getTestSuiteConfig(fileOrDirectory, fileOrDirectory); + } + + logger.log(Level.WARNING, "Unable to read file at: " + fileOrDirectory); + return Collections.emptyList(); } + /** + * Validates the list of test suite configurations. + * + * @param rootDirectory The root directory for configurations + * (relativeOutputPath is set by determining relative path from root + * directory to file). + * @param file The file containing the configuration. + * @param testSuites The list of test suite objects discovered. + * @return The test suites with invalid items filtered and relative output + * path set. + */ private List<TestSuiteConfig> validate(File rootDirectory, File file, List<TestSuiteConfig> testSuites) { return IntStream.range(0, testSuites.size()) .mapToObj(idx -> validate(rootDirectory, file, idx, testSuites.get(idx))) @@ -111,6 +185,20 @@ private List<TestSuiteConfig> validate(File rootDirectory, File file, List<TestS .collect(Collectors.toList()); } + /** + * Validates a single test suite by returning it or null if invalid. The + * relative output path is also determined based on the relative path from + * rootDirectory to file. + * + * @param rootDirectory The root directory for configurations + * (relativeOutputPath is set by determining relative path from root + * directory to file). + * @param file The file containing the configuration. + * @param index The index within a list of test suites which this item + * exists. + * @param config The test suite confi. + * @return The test suite with relative output path set or null if invalid. + */ private TestSuiteConfig validate(File rootDirectory, File file, int index, TestSuiteConfig config) { if (config == null || StringUtils.isBlank(config.getName()) diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/ConnectionConfig.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/ConnectionConfig.java index 7c477711d88f9eea00e0e046fe00875768286a31..1d55fa4c7bf21cd1228a67239a7826b8a0669950 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/ConnectionConfig.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/ConnectionConfig.java @@ -22,8 +22,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * - * @author gregd + * Configuration information for a postgres connection. */ public class ConnectionConfig { private final String hostName; @@ -31,6 +30,13 @@ public class ConnectionConfig { private final String userName; private final String password; + /** + * Main constructor. + * @param hostName The host name. + * @param port The port to use. + * @param userName The user name to use. + * @param password The password to use. + */ @JsonCreator public ConnectionConfig( @JsonProperty("hostName") String hostName, @@ -44,18 +50,30 @@ public ConnectionConfig( this.password = password; } + /** + * @return The host name. + */ public String getHostName() { return hostName; } + /** + * @return The port. + */ public Integer getPort() { return port; } + /** + * @return The user name. + */ public String getUserName() { return userName; } + /** + * @return The password to use. + */ public String getPassword() { return password; } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/EnvConfig.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/EnvConfig.java index 2432a09350e32444eb9416ef626320535c40ae61..ccfefd6ba5837aa503f6e381c203614b07ddc7f6 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/EnvConfig.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/EnvConfig.java @@ -22,8 +22,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * - * @author gregd + * Defines integration testing environment settings. */ public class EnvConfig { @@ -34,18 +33,40 @@ public class EnvConfig { private final ConnectionConfig connectionInfo; private String workingDirectory; + private Boolean useRelativeOutput; + /** + * Main constructor. + * + * @param rootCaseOutputPath The location where cases will be created. + * @param rootTestSuitesPath The location of test suite configuration + * file(s). + * @param rootTestOutputPath The location where output results should be + * created. + * @param connectionInfo The connection info for postgres. + * @param workingDirectory The working directory (if not specified, the + * parent directory of the EnvConfig file is used. + * @param useRelativeOutput If true, results will be outputted maintaining + * the same relative path structure as the file (i.e. if file was found at + * /rootTestSuitesPath/folderX/fileY.json then it will now be outputted in + * /rootTestOutputPath/folderX/fileY/) + */ @JsonCreator public EnvConfig( @JsonProperty("rootCaseOutputPath") String rootCaseOutputPath, @JsonProperty("rootTestSuitesPath") String rootTestSuitesPath, @JsonProperty("rootTestOutputPath") String rootTestOutputPath, - @JsonProperty("connectionInfo") ConnectionConfig connectionInfo) { - + @JsonProperty("connectionInfo") ConnectionConfig connectionInfo, + @JsonProperty("workingDirectory") String workingDirectory, + @JsonProperty("useRelativeOutput") Boolean useRelativeOutput) { + this.rootCaseOutputPath = rootCaseOutputPath; this.rootTestOutputPath = rootTestOutputPath; this.rootTestSuitesPath = rootTestSuitesPath; this.connectionInfo = connectionInfo; + + this.workingDirectory = workingDirectory; + this.useRelativeOutput = useRelativeOutput; } /** @@ -82,11 +103,40 @@ public void setWorkingDirectory(String workingDirectory) { this.workingDirectory = workingDirectory; } + /** + * @return The postgres connection information. + */ public ConnectionConfig getConnectionInfo() { return connectionInfo; } + /** + * @return The root test suites path that will be searched or the path to a + * single file. + */ public String getRootTestSuitesPath() { return rootTestSuitesPath; } + + /** + * @return If true, results will be outputted maintaining the same relative + * path structure as the file (i.e. if file was found at + * /rootTestSuitesPath/folderX/fileY.json then it will now be outputted in + * /rootTestOutputPath/folderX/fileY/) + */ + public boolean getUseRelativeOutput() { + return Boolean.TRUE.equals(useRelativeOutput); + } + + /** + * Sets whether or not to use the relative output path. + * + * @param useRelativeOutput If true, results will be outputted maintaining + * the same relative path structure as the file (i.e. if file was found at + * /rootTestSuitesPath/folderX/fileY.json then it will now be outputted in + * /rootTestOutputPath/folderX/fileY/) + */ + public void setUseRelativeOutput(boolean useRelativeOutput) { + this.useRelativeOutput = useRelativeOutput; + } } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/IntegrationTestConfig.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/IntegrationTestConfig.java index c8bd0f6532538910649677ce6bca0fa30e612596..b481428cddff811a4463e0462cd42354c874d9e1 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/IntegrationTestConfig.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/IntegrationTestConfig.java @@ -31,6 +31,11 @@ public class IntegrationTestConfig { private final List<TestSuiteConfig> testSuites; private final EnvConfig envConfig; + /** + * Main constructor. + * @param testSuites The test suites to be run. + * @param envConfig The environment configuration. + */ @JsonCreator public IntegrationTestConfig( @JsonProperty("testSuites") List<TestSuiteConfig> testSuites, @@ -41,12 +46,15 @@ public IntegrationTestConfig( } /** - * @return The per-case configuration. + * @return A list of test suite configurations. */ public List<TestSuiteConfig> getTestSuites() { return testSuites; } + /** + * @return The integration test environment configuration. + */ public EnvConfig getEnvConfig() { return envConfig; } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/ParameterizedResourceConfig.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/ParameterizedResourceConfig.java index 520b0771dd63ed0dd4b31f0bcf01fe3913b28417..cbbfad96f5b777d764e8461806b563fc3e1f8de4 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/ParameterizedResourceConfig.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/ParameterizedResourceConfig.java @@ -37,24 +37,37 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import org.sleuthkit.autopsy.integrationtesting.config.ParameterizedResourceConfig.ParameterizedResourceConfigDeserializer; /** - * - * @author gregd + * A resource that potentially has parameters as well. */ -@JsonDeserialize(using = ParameterizedResourceConfigDeserializer.class) +@JsonDeserialize(using = ParameterizedResourceConfig.ParameterizedResourceConfigDeserializer.class) public class ParameterizedResourceConfig { + /** + * Deserializes from json. If a string is specified, that will be the + * resource, otherwise, an object of { resource: string, parameters, {...} } + * should be specified. The parameters can be expected to be a + * Map<String, Object>, containing nested Maps, List<Object>, or json + * primitives of type String, Integer, Long, Boolean, or Double. + */ public static class ParameterizedResourceConfigDeserializer extends StdDeserializer<ParameterizedResourceConfig> { private static TypeReference<HashMap<String, Object>> typeRef = new TypeReference<HashMap<String, Object>>() { }; + /** + * Main constructor. + */ public ParameterizedResourceConfigDeserializer() { this(null); } + /** + * Main constructor specifying type. + * + * @param vc The type. + */ public ParameterizedResourceConfigDeserializer(Class<?> vc) { super(vc); } @@ -63,11 +76,14 @@ public ParameterizedResourceConfigDeserializer(Class<?> vc) { public ParameterizedResourceConfig deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { JsonNode node = jp.getCodec().readTree(jp); + // if no node, return null. if (node == null) { return null; } else if (node instanceof TextNode) { + // if just a string, return a ParameterizedResourceConfig where the resource is the string. return new ParameterizedResourceConfig(((TextNode) node).textValue()); } else { + // otherwise, determine the resource and create an object JsonNode resourceNode = node.get("resource"); String resource = (resourceNode != null) ? resourceNode.asText() : null; @@ -81,6 +97,12 @@ public ParameterizedResourceConfig deserialize(JsonParser jp, DeserializationCon } } + /** + * Reads an Object node into a Map<String, Object>. + * + * @param node The json node. + * @return The Map<String, Object>. + */ Map<String, Object> readMap(ObjectNode node) { Map<String, Object> jsonObject = new LinkedHashMap<>(); Iterator<Map.Entry<String, JsonNode>> keyValIter = node.fields(); @@ -91,6 +113,12 @@ Map<String, Object> readMap(ObjectNode node) { return jsonObject; } + /** + * Reads an Array node into a List<Object>. + * + * @param node The json array node. + * @return The list of objects. + */ List<Object> readList(ArrayNode node) { List<Object> objArr = new ArrayList<>(); for (JsonNode childNode : node) { @@ -99,6 +127,13 @@ List<Object> readList(ArrayNode node) { return objArr; } + /** + * Reads a Json value node into an Object (text, boolean, long, int, + * double). + * + * @param vNode The value node. + * @return The created object. + */ Object readJsonPrimitive(ValueNode vNode) { if (vNode.isTextual()) { return vNode.asText(); @@ -115,6 +150,12 @@ Object readJsonPrimitive(ValueNode vNode) { return null; } + /** + * Reads a json node of unknown type into a java object. + * + * @param node The json node. + * @return The object. + */ Object readItem(JsonNode node) { if (node == null) { return null; @@ -135,19 +176,38 @@ Object readItem(JsonNode node) { private final String resource; private final Map<String, Object> parameters; + /** + * Main constructor. + * + * @param resource The resource name. + * @param parameters The parameters to be specified. + */ public ParameterizedResourceConfig(String resource, Map<String, Object> parameters) { this.resource = resource; this.parameters = (parameters == null) ? Collections.emptyMap() : parameters; } + /** + * Main constructor where parameters are null. + * + * @param resource The resource. + */ public ParameterizedResourceConfig(String resource) { this(resource, null); } + /** + * @return The resource identifier. + */ public String getResource() { return resource; } + /** + * @return Parameters provided for the resource. Nested objects will be + * Map<String, Object>, List<Object> or a json primitive like boolean, int, + * long, double, string. + */ public Map<String, Object> getParameters() { return parameters; } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/TestSuiteConfig.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/TestSuiteConfig.java index 22ab1e68ebc599d9fbed857692db4066e6ea51c8..51132c9feb0c6c776119fd87760798b37f9e149c 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/TestSuiteConfig.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/TestSuiteConfig.java @@ -23,7 +23,7 @@ import java.util.List; /** - * Configuration in IntegrationTests per case. + * Configuration per test suite. */ public class TestSuiteConfig { @@ -35,6 +35,17 @@ public class TestSuiteConfig { private final IntegrationCaseType caseTypes; private String relativeOutputPath; + /** + * Main constructor. + * + * @param name Name of the test suite. + * @param description The description for the test suite. + * @param dataSources The data sources to use. + * @param configurationModules The modules in order to configure the autopsy + * environment. + * @param integrationTests The integration tests to run. + * @param caseTypes The case types (single user, multi user, both). + */ @JsonCreator public TestSuiteConfig( @JsonProperty("name") String name, @@ -52,34 +63,68 @@ public TestSuiteConfig( this.caseTypes = caseTypes; } + /** + * @return The name of the test suite. + */ public String getName() { return name; } + /** + * @return The description of the test suite. + */ public String getDescription() { return description; } + /** + * @return The data sources to be ingested. + */ public List<String> getDataSources() { return dataSources; } + /** + * @return The configuration modules to be run to set up the autopsy + * environment. + */ public List<ParameterizedResourceConfig> getConfigurationModules() { return configurationModules; } + /** + * @return The configuration for integration tests to run for output. + */ public TestingConfig getIntegrationTests() { return integrationTests; } + /** + * @return The case type (single user, multi user, both). + */ public IntegrationCaseType getCaseTypes() { return caseTypes; } + /** + * @return The relative output path used if EnvConfig.useRelativeOutputPath + * is true. If file is found at /testSuitePath/relPathX/fileY.json, and + * EnvConfig.useRelativeOutputPath is true, then output results will be + * located in /outputPath/relPathX/fileY/. + */ public String getRelativeOutputPath() { return relativeOutputPath; } + /** + * Sets the relative output path. + * + * @param relativeOutputPath The relative output path used if + * EnvConfig.useRelativeOutputPath is true. If file is found at + * /testSuitePath/relPathX/fileY.json, and EnvConfig.useRelativeOutputPath + * is true, then output results will be located in + * /outputPath/relPathX/fileY/. + */ public void setRelativeOutputPath(String relativeOutputPath) { this.relativeOutputPath = relativeOutputPath; } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/TestingConfig.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/TestingConfig.java index a2a9b165adc7cf73521321e39ab2e86ff69618b1..b42416bf382f8a66fa2bed7a9634d4486ae37483 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/TestingConfig.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/integrationtesting/config/TestingConfig.java @@ -38,16 +38,23 @@ public class TestingConfig { private final Map<String, ParameterizedResourceConfig> excludeAllExcept; private final Set<String> includeAllExcept; + /** + * Main constructor for Integration tests to be run. + * + * @param excludeAllExcept Items that should be run to the exclusion of all + * others. + * @param includeAllExcept Items that should only be run. + */ @JsonCreator public TestingConfig( - @JsonProperty("excludeAllExcept") List<ParameterizedResourceConfig> excludeAllExcept, + @JsonProperty("excludeAllExcept") List<ParameterizedResourceConfig> excludeAllExcept, @JsonProperty("includeAllExcept") List<String> includeAllExcept) { List<ParameterizedResourceConfig> safeExcludeAllExcept = ((excludeAllExcept == null) ? Collections.emptyList() : excludeAllExcept); this.excludeAllExcept = safeExcludeAllExcept .stream() .collect(Collectors.toMap( - (res) -> res.getResource() == null ? "" : res.getResource().toUpperCase(), + (res) -> res.getResource() == null ? "" : res.getResource().toUpperCase(), (res) -> res, (res1, res2) -> { Map<String, Object> mergedArgs = new HashMap<>(); @@ -55,7 +62,7 @@ public TestingConfig( mergedArgs.putAll(res2.getParameters()); return new ParameterizedResourceConfig(res1.getResource(), mergedArgs); }) - ); + ); List<String> safeIncludeAllExcept = ((includeAllExcept == null) ? Collections.emptyList() : includeAllExcept); this.includeAllExcept = safeIncludeAllExcept @@ -80,7 +87,7 @@ public Set<ParameterizedResourceConfig> getExcludeAllExcept() { public Set<String> getIncludeAllExcept() { return includeAllExcept; } - + public Map<String, Object> getParameters(String itemType) { ParameterizedResourceConfig resource = (itemType == null) ? null : excludeAllExcept.get(itemType.toUpperCase()); return resource == null ? Collections.emptyMap() : new HashMap<String, Object>(resource.getParameters());