diff --git a/class_diagram.uml b/class_diagram.uml index 4df5cf7..9aed5fa 100644 --- a/class_diagram.uml +++ b/class_diagram.uml @@ -3,510 +3,176 @@ JAVA testsmell - testsmell.smell.ExceptionCatchingThrowing - testsmell.smell.ConstructorInitialization - testsmell.smell.MagicNumberTest - testsmell.Util - testsmell.smell.AssertionRoulette - testsmell.smell.EmptyTest - testsmell.smell.UnknownTest - testsmell.AbstractSmell - testsmell.smell.DuplicateAssert - testsmell.smell.ResourceOptimism - testsmell.smell.EagerTest - testsmell.smell.PrintStatement - testsmell.smell.SensitiveEquality - testsmell.smell.DefaultTest - testsmell.TestSmellDetector - testsmell.smell.MysteryGuest - testsmell.TestMethod - testsmell.SmellyElement - testsmell.smell.RedundantAssertion - testsmell.smell.IgnoredTest - testsmell.smell.VerboseTest - testsmell.smell.LazyTest - testsmell.smell.SleepyTest - testsmell.ResultsWriter - testsmell.TestFile - testsmell.smell.GeneralFixture - testsmell.TestClass - testsmell.smell.DependentTest - testsmell.smell.ConditionalTestLogic + testsmell.smell.ExceptionCatchingThrowing + testsmell.smell.ConstructorInitialization + testsmell.smell.MagicNumberTest + testsmell.Util + testsmell.smell.AssertionRoulette + testsmell.smell.EmptyTest + testsmell.smell.UnknownTest + testsmell.AbstractSmell + testsmell.smell.DuplicateAssert + testsmell.smell.ResourceOptimism + testsmell.smell.EagerTest + testsmell.smell.PrintStatement + testsmell.smell.SensitiveEquality + testsmell.smell.DefaultTest + testsmell.TestSmellDetector + testsmell.smell.MysteryGuest + testsmell.TestMethod + testsmell.SmellyElement + testsmell.smell.RedundantAssertion + testsmell.smell.IgnoredTest + testsmell.smell.VerboseTest + testsmell.smell.SleepyTest + testsmell.smell.LazyTest + testsmell.ResultsWriter + testsmell.TestFile + testsmell.smell.GeneralFixture + testsmell.TestClass + testsmell.smell.DependentTest + testsmell.smell.ConditionalTestLogic - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - + + + + - - - - + + + + - - - - + + + + - - - - - - - - - - + + + + - - - - - - - - - - - - + + + + - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - + + + + - - - - - + + + + + - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - + + + + - - - - + + + + - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - + + + + - + Constructors diff --git a/pom.xml b/pom.xml index 879b4e7..c03dc09 100644 --- a/pom.xml +++ b/pom.xml @@ -3,12 +3,26 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - edu.rit.se.testsmells TestSmellDetector - 0.1 + 0.3.2 jar + + edu.rit.se.testsmells.Main + + + + src/resources + true + + + + + src/resources + true + + org.apache.maven.plugins @@ -29,6 +43,21 @@ 5.4.2 + + integration + --illegal-access=permit + + + + org.apache.maven.plugins + maven-jar-plugin + + + + ${project.MainClass} + + + org.apache.maven.plugins @@ -42,9 +71,7 @@ - - Main - + ${project.MainClass} @@ -56,6 +83,23 @@ + + + integration + + + + org.apache.maven.plugins + maven-surefire-plugin + + integration + none + + + + + + @@ -80,6 +124,12 @@ 5.4.2 test + + org.mockito + mockito-core + 3.5.10 + test + org.junit.jupiter junit-jupiter-engine @@ -87,5 +137,5 @@ test - + \ No newline at end of file diff --git a/src/main/java/Main.java b/src/main/java/Main.java deleted file mode 100644 index 94f6786..0000000 --- a/src/main/java/Main.java +++ /dev/null @@ -1,112 +0,0 @@ -import testsmell.AbstractSmell; -import testsmell.ResultsWriter; -import testsmell.TestFile; -import testsmell.TestSmellDetector; -import testsmell.smell.AssertionRoulette; -import testsmell.smell.EagerTest; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -public class Main { - public static void main(String[] args) throws IOException { - if (args == null) { - System.out.println("Please provide the file containing the paths to the collection of test files"); - return; - } - if(!args[0].isEmpty()){ - File inputFile = new File(args[0]); - if(!inputFile.exists() || inputFile.isDirectory()) { - System.out.println("Please provide a valid file containing the paths to the collection of test files"); - return; - } - } - - TestSmellDetector testSmellDetector = new TestSmellDetector(); - - /* - Read the input file and build the TestFile objects - */ - BufferedReader in = new BufferedReader(new FileReader(args[0])); - String str; - - String[] lineItem; - TestFile testFile; - List testFiles = new ArrayList<>(); - while ((str = in.readLine()) != null) { - // use comma as separator - lineItem = str.split(","); - - //check if the test file has an associated production file - if(lineItem.length ==2){ - testFile = new TestFile(lineItem[0], lineItem[1], ""); - } - else{ - testFile = new TestFile(lineItem[0], lineItem[1], lineItem[2]); - } - - testFiles.add(testFile); - } - - /* - Initialize the output file - Create the output file and add the column names - */ - ResultsWriter resultsWriter = ResultsWriter.createResultsWriter(); - List columnNames; - List columnValues; - - columnNames = testSmellDetector.getTestSmellNames(); - columnNames.add(0, "App"); - columnNames.add(1, "TestClass"); - columnNames.add(2, "TestFilePath"); - columnNames.add(3, "ProductionFilePath"); - columnNames.add(4, "RelativeTestFilePath"); - columnNames.add(5, "RelativeProductionFilePath"); - - resultsWriter.writeColumnName(columnNames); - - /* - Iterate through all test files to detect smells and then write the output - */ - TestFile tempFile; - DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); - Date date; - for (TestFile file : testFiles) { - date = new Date(); - System.out.println(dateFormat.format(date) + " Processing: "+file.getTestFilePath()); - System.out.println("Processing: "+file.getTestFilePath()); - - //detect smells - tempFile = testSmellDetector.detectSmells(file); - - //write output - columnValues = new ArrayList<>(); - columnValues.add(file.getApp()); - columnValues.add(file.getTestFileName()); - columnValues.add(file.getTestFilePath()); - columnValues.add(file.getProductionFilePath()); - columnValues.add(file.getRelativeTestFilePath()); - columnValues.add(file.getRelativeProductionFilePath()); - for (AbstractSmell smell : tempFile.getTestSmells()) { - try { - columnValues.add(String.valueOf(smell.getHasSmell())); - } - catch (NullPointerException e){ - columnValues.add(""); - } - } - resultsWriter.writeLine(columnValues); - } - - System.out.println("end"); - } - - -} diff --git a/src/main/java/edu/rit/se/testsmells/Main.java b/src/main/java/edu/rit/se/testsmells/Main.java new file mode 100644 index 0000000..2195d21 --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/Main.java @@ -0,0 +1,102 @@ +package edu.rit.se.testsmells; + +import edu.rit.se.testsmells.testsmell.CSVWriter; +import edu.rit.se.testsmells.testsmell.ReportController; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import edu.rit.se.testsmells.testsmell.smell.*; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class Main { + public static void main(String[] args) throws IOException { + File inputFile = handleCliArgs(args); + + TestSmellDetector testSmellDetector = initializeSmells(); + + List files = readInputTestFiles(inputFile); + CSVWriter csvWriter = CSVWriter.createResultsWriter(); + ReportController reportCtrl = ReportController.createReportController(csvWriter); + + for (TestFile file : files) { + System.out.println(getCurrentDateFormatted() + " Processing: " + file.getTestFilePath()); + testSmellDetector.detectSmells(file); + } + + reportCtrl.report(files); + + System.out.println("end"); + } + + private static Object getCurrentDateFormatted() { + return (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()); + } + + private static List readInputTestFiles(File inputFile) throws IOException { + BufferedReader in = new BufferedReader(new FileReader(inputFile)); + String str; + + String[] lineItem; + TestFile testFile; + List testFiles = new ArrayList<>(); + while ((str = in.readLine()) != null) { + // use comma as separator + lineItem = str.split(","); + + //check if the test file has an associated production file + if (lineItem.length == 2) { + testFile = new TestFile(lineItem[0], lineItem[1], ""); + } else { + testFile = new TestFile(lineItem[0], lineItem[1], lineItem[2]); + } + + testFiles.add(testFile); + } + return testFiles; + } + + private static File handleCliArgs(String[] args) { + assert args != null && args.length > 0 && args[0].isEmpty() : "Please provide the file containing the paths to the collection of test files"; + File inputFile = new File(args[0]); + assert inputFile.exists() && !inputFile.isDirectory() : "Please provide a valid file containing the paths to the collection of test files"; + + return inputFile; + } + + private static TestSmellDetector initializeSmells() { + TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new AssertionRoulette()); + testSmellDetector.addDetectableSmell(new ConditionalTestLogic()); + testSmellDetector.addDetectableSmell(new ConstructorInitialization()); + testSmellDetector.addDetectableSmell(new DefaultTest()); + testSmellDetector.addDetectableSmell(new EmptyTest()); + testSmellDetector.addDetectableSmell(new ExceptionCatchingThrowing()); + testSmellDetector.addDetectableSmell(new GeneralFixture()); + testSmellDetector.addDetectableSmell(new MysteryGuest()); + testSmellDetector.addDetectableSmell(new PrintStatement()); + testSmellDetector.addDetectableSmell(new RedundantAssertion()); + testSmellDetector.addDetectableSmell(new SensitiveEquality()); + testSmellDetector.addDetectableSmell(new VerboseTest()); + testSmellDetector.addDetectableSmell(new SleepyTest()); + testSmellDetector.addDetectableSmell(new EagerTest()); + testSmellDetector.addDetectableSmell(new LazyTest()); + testSmellDetector.addDetectableSmell(new DuplicateAssert()); + testSmellDetector.addDetectableSmell(new UnknownTest()); + testSmellDetector.addDetectableSmell(new IgnoredTest()); + testSmellDetector.addDetectableSmell(new ResourceOptimism()); + testSmellDetector.addDetectableSmell(new MagicNumberTest()); + testSmellDetector.addDetectableSmell(new DependentTest()); + + return testSmellDetector; + } + + +} diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java new file mode 100644 index 0000000..3928fbe --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java @@ -0,0 +1,100 @@ +package edu.rit.se.testsmells.testsmell; + +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.PackageDeclaration; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.expr.Name; +import com.github.javaparser.ast.expr.ObjectCreationExpr; +import com.github.javaparser.ast.nodeTypes.NodeWithSimpleName; + +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +public abstract class AbstractSmell { + private final MethodValidator methodValidator; + private final Set smellyElementList; + + public abstract String getSmellName(); + + public AbstractSmell() { + methodValidator = MethodValidator.getInstance(); + smellyElementList = new CopyOnWriteArraySet<>(); + } + + public abstract AbstractSmell recreate(); + + protected String getFullMethodName(CompilationUnit unit, T node) { + String className; + try { + className = node.getParentNode().map(x -> (ClassOrInterfaceDeclaration) x).map(NodeWithSimpleName::getNameAsString).orElse(""); + } catch (ClassCastException exception) { + if (node.getParentNode().isPresent() && node.getParentNode().get() instanceof ObjectCreationExpr) { + className = ((ObjectCreationExpr) node.getParentNode().get()).getType().getNameAsString() + "##AnonymousClass"; + System.err.println("Anonymous function detected! Using interface name with \"##AnonymousClass\" suffix."); + } else { + System.err.println("Failed solving " + node.getNameAsString() + " parent's class name. Expected method within a class and received " + node.getClass() + " whose parent is " + (node.getParentNode().isPresent() ? "of type " + node.getParentNode().get().getClass() : "unavailable.")); + throw exception; + } + } + + return getPackageClass(unit) + className + "." + node.getNameAsString(); + } + + protected String getFullClassName(CompilationUnit unit, NodeWithSimpleName node) { + return getPackageClass(unit) + node.getNameAsString(); + } + + private String getPackageClass(CompilationUnit unit) { + return unit.getPackageDeclaration().map(PackageDeclaration::getName).map(Name::asString).map(x -> x + ".").orElse(""); + } + + public abstract void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException; + + public void clear() { + for (SmellyElement smellyElement : smellyElementList) { + smellyElement.clear(); + } + smellyElementList.clear(); + } + + /** + * Returns the set of analyzed elements (i.e. test methods) + */ + public List getSmellyElements() { + return new ArrayList<>(smellyElementList); + } + + public void addSmellyElement(SmellyElement elem) { + smellyElementList.add(elem); + elem.addDetectedSmell(this); + } + + /** + * Returns true if any of the elements has a smell + */ + public boolean hasSmell() { + return smellyElementList.stream().anyMatch(SmellyElement::hasSmell); + } + + protected boolean isNumber(String str) { + try { + Double.parseDouble(str); + return true; + } catch (NumberFormatException nfe) { + return false; + } + } + + protected boolean isValidTestMethod(MethodDeclaration method) { + return methodValidator.isValidTestMethod(method); + } + + protected boolean isValidSetupMethod(MethodDeclaration method) { + return methodValidator.isValidSetupMethod(method); + } +} diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/CSVWriter.java b/src/main/java/edu/rit/se/testsmells/testsmell/CSVWriter.java new file mode 100644 index 0000000..657322d --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/CSVWriter.java @@ -0,0 +1,106 @@ +package edu.rit.se.testsmells.testsmell; + +import java.io.FileWriter; +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.stream.Collectors; + +/** + * This class is utilized to write output to a CSV file + */ +public class CSVWriter { + + private String suffix; + private FileWriter writer; + private List headers; + private String name; + + /** + * Creates the file into which output it to be written into. Results from each file will be stored in a new file + * + */ + private CSVWriter() { + String time = String.valueOf(Calendar.getInstance().getTimeInMillis()); + suffix = MessageFormat.format("{0}_{1}_{2}.{3}", "Output", "TestSmellDetection", time, "csv"); + writer = null; + } + + /** + * Factory method that provides a new instance of the ResultsWriter + * + * @return new ResultsWriter instance + */ + public static CSVWriter createResultsWriter() { + return new CSVWriter(); + } + + void setOutputFilePrefix(String prefix) throws IOException { + name = MessageFormat.format("{0}_{1}", prefix, suffix); + writer = new FileWriter(name, true); + } + + public String getSuffix() { + return suffix; + } + + String getFilename() { + return name; + } + + public void writeCSVHeader(List reports) throws IOException { + headers = reports.stream().flatMap(report -> report.getEntryKeys().stream()).distinct().collect(Collectors.toList()); + writeCSV(headers); + } + + public void writeCSVHeader(SmellsContainer anyFile) throws IOException { + List headers = new ArrayList<>(anyFile.getTestDescriptionEntries().keySet()); + headers.addAll(anyFile.getTestSmells().stream().map(AbstractSmell::getSmellName).collect(Collectors.toList())); + writeCSV(headers); + } + + void exportSmells(List reports) throws IOException { + for (Report report : reports) { + List entries = new ArrayList<>(); + for (String column : headers) { + entries.add(report.getValue(column)); + } + writeCSV(entries); + } + } + + void exportSmells(SmellsContainer fileTestSmells) throws IOException { + List entries = new ArrayList<>(fileTestSmells.getTestDescriptionEntries().values()); + for (AbstractSmell smell : fileTestSmells.getTestSmells()) { + try { + entries.add(String.valueOf(smell.hasSmell())); + } catch (NullPointerException e) { + entries.add(""); + } + } + writeCSV(entries); + } + + /** + * Appends the input values into the CSV file + * + * @param dataValues the data that needs to be written into the file + * @throws IOException Failed to create/open output file + */ + private void writeCSV(List dataValues) throws IOException { + for (int i = 0; i < dataValues.size(); i++) { + writer.append(String.valueOf(dataValues.get(i))); + + if (i != dataValues.size() - 1) writer.append(","); + else writer.append(System.lineSeparator()); + + } + } + + void closeFile() throws IOException { + writer.flush(); + writer.close(); + } +} diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ExtractingStrategy.java b/src/main/java/edu/rit/se/testsmells/testsmell/ExtractingStrategy.java new file mode 100644 index 0000000..d4c5170 --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ExtractingStrategy.java @@ -0,0 +1,10 @@ +package edu.rit.se.testsmells.testsmell; + +import java.util.List; + +/** + * Report informations extractor + */ +public interface ExtractingStrategy { + List extract(List smells, Class type); +} diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/MethodValidator.java b/src/main/java/edu/rit/se/testsmells/testsmell/MethodValidator.java new file mode 100644 index 0000000..cfc3ead --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/MethodValidator.java @@ -0,0 +1,49 @@ +package edu.rit.se.testsmells.testsmell; + +import com.github.javaparser.ast.Modifier; +import com.github.javaparser.ast.body.MethodDeclaration; + +public class MethodValidator { + private static MethodValidator instance; + + private MethodValidator() { + } + + public static MethodValidator getInstance() { + if (instance == null) { + instance = new MethodValidator(); + } + return instance; + } + + public boolean isValidTestMethod(MethodDeclaration method) { + return isVisible(method) && (hasAnnotation(method, "Test") || nameStartsWith(method, "test")); + } + + public boolean isValidSetupMethod(MethodDeclaration method) { + return isVisible(method) && (hasAnnotation(method, "Before") || hasAnnotation(method, "BeforeEach") || nameStartsWith(method, "setUp")); + } + + private boolean nameStartsWith(MethodDeclaration method, String value) { + String method_name = method.getNameAsString().toLowerCase(); + String expected_name = value.toLowerCase(); + + return method_name.startsWith(expected_name); + } + + private boolean isVisible(MethodDeclaration method) { + return !isIgnored(method) && isPublic(method); + } + + private boolean hasAnnotation(MethodDeclaration method, String annotation) { + return method.getAnnotationByName(annotation).isPresent(); + } + + private boolean isPublic(MethodDeclaration method) { + return method.getModifiers().contains(Modifier.PUBLIC); + } + + private boolean isIgnored(MethodDeclaration method) { + return hasAnnotation(method, "Ignore"); + } +} \ No newline at end of file diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/Report.java b/src/main/java/edu/rit/se/testsmells/testsmell/Report.java new file mode 100644 index 0000000..6f70c57 --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/Report.java @@ -0,0 +1,11 @@ +package edu.rit.se.testsmells.testsmell; + +import java.util.List; + +public interface Report { + List getEntryValues(); + + List getEntryKeys(); + + String getValue(String key); +} diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java new file mode 100644 index 0000000..3d04dc9 --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java @@ -0,0 +1,93 @@ +package edu.rit.se.testsmells.testsmell; + +import edu.rit.se.testsmells.testsmell.internal.Extractor; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Properties; +import java.util.stream.Collectors; + +public class ReportController { + private final CSVWriter csvWriter; + private final List configuredGranularities; + private ExtractingStrategy extractor = new Extractor(); + + ReportController(CSVWriter csvWriter, List granularities) { + this.csvWriter = csvWriter; + + configuredGranularities = granularities; + } + + public static ReportController createReportController(CSVWriter csvWriter) throws IOException { + return new ReportController(csvWriter, readProperties()); + } + + private static List readProperties() throws IOException { + final String PROPERTIES_FILENAME = "test-smells.properties"; + final String PROPERTIES_KEY = "report.granularity"; + Properties prop = new Properties(); + prop.load(Objects.requireNonNull(ReportController.class.getClassLoader().getResourceAsStream(PROPERTIES_FILENAME))); + String granularityConfig = prop.getProperty(PROPERTIES_KEY); + return Arrays.stream(granularityConfig.split(",")).map(ReportGranularity::valueOf).collect(Collectors.toList()); + } + + public void report(List files) throws IOException { + for (ReportGranularity config : configuredGranularities) { + csvWriter.setOutputFilePrefix(config.toString()); + switch (config) { + case CLASS: + reportTestClasses(files); + break; + case METHOD: + reportTestMethods(files); + break; + case FILE: + reportTestFiles(files); + break; + default: + throw new IllegalStateException("Unexpected value: " + config); + } + csvWriter.closeFile(); + } + } + + private void reportTestMethods(List files) throws IOException { + if (files.size() > 0) { + csvWriter.writeCSVHeader(mergeSmellyElements(files.get(0).getTestSmells(), TestMethod.class)); + for (TestFile file : files) { + reportSmellyElements(file.getTestSmells(), TestMethod.class); + } + } + } + + private void reportTestClasses(List files) throws IOException { + if (files.size() > 0) { + csvWriter.writeCSVHeader(mergeSmellyElements(files.get(0).getTestSmells(), TestClass.class)); + for (TestFile file : files) { + reportSmellyElements(file.getTestSmells(), TestClass.class); + } + } + } + + private void reportTestFiles(List files) throws IOException { + if (files.size() > 0) { + csvWriter.writeCSVHeader(files.get(0)); + for (TestFile file : files) { + csvWriter.exportSmells(file); + } + } + } + + private List mergeSmellyElements(List smells, Class type) { + return extractor.extract(smells, type); + } + + private void reportSmellyElements(List smells, Class type) throws IOException { + List smellyElementReports = mergeSmellyElements(smells, type); + csvWriter.exportSmells(smellyElementReports); + } + + enum ReportGranularity {FILE, CLASS, METHOD} +} diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java b/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java new file mode 100644 index 0000000..fd74911 --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java @@ -0,0 +1,21 @@ +package edu.rit.se.testsmells.testsmell; + +import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.stream.Collectors; + +public abstract class SmellsContainer { + public final Set testSmells = new CopyOnWriteArraySet<>(); + + public abstract Map getTestDescriptionEntries(); + + public void addDetectedSmell(AbstractSmell newSmell) { + assert Objects.nonNull(newSmell); + testSmells.add(newSmell); + } + + public List getTestSmells() { + return new ArrayList<>(testSmells); + } +} diff --git a/src/main/java/testsmell/TestClass.java b/src/main/java/edu/rit/se/testsmells/testsmell/SmellyElement.java similarity index 56% rename from src/main/java/testsmell/TestClass.java rename to src/main/java/edu/rit/se/testsmells/testsmell/SmellyElement.java index df55952..dc64f68 100644 --- a/src/main/java/testsmell/TestClass.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/SmellyElement.java @@ -1,17 +1,20 @@ -package testsmell; +package edu.rit.se.testsmells.testsmell; import java.util.HashMap; import java.util.Map; -public class TestClass extends SmellyElement { - - private String className; +public abstract class SmellyElement extends SmellsContainer { + private final String name; + private final Map data; private boolean hasSmell; - private Map data; - public TestClass(String className) { - this.className = className; + public SmellyElement(String name) { data = new HashMap<>(); + this.name = name; + } + + public void clear() { + data.clear(); } public void setHasSmell(boolean hasSmell) { @@ -22,17 +25,14 @@ public void addDataItem(String name, String value) { data.put(name, value); } - @Override public String getElementName() { - return className; + return name; } - @Override - public boolean getHasSmell() { + public boolean hasSmell() { return hasSmell; } - @Override public Map getData() { return data; } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestClass.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestClass.java new file mode 100644 index 0000000..c8f1de1 --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestClass.java @@ -0,0 +1,20 @@ +package edu.rit.se.testsmells.testsmell; + +import java.util.HashMap; +import java.util.Map; + +public class TestClass extends SmellyElement { + + public TestClass(String name) { + super(name); + } + + @Override + public Map getTestDescriptionEntries() { + Map entries = new HashMap<>(getData()); + + entries.put("Name", getElementName()); + + return entries; + } +} diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java new file mode 100644 index 0000000..4b2c445 --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java @@ -0,0 +1,132 @@ +package edu.rit.se.testsmells.testsmell; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +public class TestFile extends SmellsContainer { + private final String app, testFilePath, productionFilePath; + private boolean isProductionFileOmitted = false; + + public TestFile(String app, String testFilePath, String productionFilePath) { + checkValidity(testFilePath, productionFilePath, app); + this.app = app; + this.testFilePath = testFilePath; + this.productionFilePath = productionFilePath; + } + + /** + * Apply validation checks on constructor params. + * It should be overridable for test purpose + * + * @param testPath Test file path (must contain file extension and be in a subfolder) + * @param prodPath Production file path (must contain file extension and be in a subfolder) + * @param app Project name (cannot be empty) + */ + protected void checkValidity(String testPath, String prodPath, String app) { + if (prodPath.isEmpty()) { + isProductionFileOmitted = true; + } + if (!haveExtension(testPath, prodPath) || !haveFileSeparator(testPath, prodPath)) { + throw new IllegalArgumentException("Both testFilePath and productionFilePath should include extensions and file separator."); + } + if (app.isEmpty()) { + throw new IllegalArgumentException("App cannot be empty!"); + } + } + + private boolean haveFileSeparator(String testPath, String prodPath) { + return testPath.lastIndexOf(File.separator) != -1 && (isProductionFileOmitted || prodPath.lastIndexOf(File.separator) != -1); + } + + private boolean haveExtension(String testPath, String prodPath) { + return testPath.lastIndexOf('.') != -1 && (isProductionFileOmitted || prodPath.lastIndexOf('.') != -1); + } + + /** + * Retrieve each description property name and getter method in a HashMap + * + * @return A Map of description properties + */ + public Map getTestDescriptionEntries() { + Map descriptions = new HashMap<>(); + + descriptions.put("App", getApp()); + descriptions.put("TestFileName", getTestFileName()); + descriptions.put("TestFilePath", getTestFilePath()); + descriptions.put("ProductionFilePath", getProductionFilePath()); + descriptions.put("ProductionFileName", getProductionFileName()); + descriptions.put("RelativeTestFilePath", getRelativeTestFilePath()); + descriptions.put("RelativeProductionFilePath", getRelativeProductionFilePath()); + + return descriptions; + } + + public String getApp() { + return app; + } + + public String getProductionFilePath() { + return productionFilePath; + } + + public String getTestFilePath() { + return testFilePath; + } + + public String getTestFileNameWithoutExtension() { + return removeExtension(getTestFileName()); + } + + public String getProductionFileNameWithoutExtension() { + if (isProductionFileOmitted) { + return ""; + } + return removeExtension(getProductionFileName()); + } + + private String removeExtension(String filename) { + return filename.substring(0, filename.lastIndexOf(".")); + } + + private String extractFileFromPath(String path) { + return path.substring(path.lastIndexOf(File.separator) + 1); + } + + public String getTestFileName() { + return extractFileFromPath(testFilePath); + } + + public String getProductionFileName() { + if (isProductionFileOmitted) { + return ""; + } + return extractFileFromPath(productionFilePath); + } + + /** + * Returns the path of the test file relative to the folder with the name of the project. + * If the project directory has a different name, returns an empty string. + * + * @return the relative test file path + */ + public String getRelativeTestFilePath() { + return extractRelativePathFrom(testFilePath); + } + + /** + * Returns the path of the production file relative to the folder with the name of the project. + * If the project directory has a different name, returns an empty string. + * + * @return the relative production file path + */ + public String getRelativeProductionFilePath() { + return extractRelativePathFrom(productionFilePath); + } + + private String extractRelativePathFrom(String path) { + int projectNameIndex = path.indexOf(app); + if (projectNameIndex == -1) return ""; + return path.substring(projectNameIndex + app.length() + File.separator.length()); + } +} \ No newline at end of file diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestMethod.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestMethod.java new file mode 100644 index 0000000..bad8d8d --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestMethod.java @@ -0,0 +1,20 @@ +package edu.rit.se.testsmells.testsmell; + +import java.util.HashMap; +import java.util.Map; + +public class TestMethod extends SmellyElement { + + public TestMethod(String name) { + super(name); + } + + @Override + public Map getTestDescriptionEntries() { + Map entries = new HashMap<>(getData()); + + entries.put("Name", getElementName()); + + return entries; + } +} diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java new file mode 100644 index 0000000..2df0517 --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java @@ -0,0 +1,100 @@ +package edu.rit.se.testsmells.testsmell; + +import com.github.javaparser.JavaParser; +import com.github.javaparser.ast.CompilationUnit; +import org.apache.commons.lang3.StringUtils; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public final class TestSmellDetector { + + private List inputStreams; + private List testSmells; + + /** + * Instantiates the various test smell analyzer classes and loads the objects into an List + */ + public TestSmellDetector() { + testSmells = new ArrayList<>(); + inputStreams = new ArrayList<>(); + } + + public void addDetectableSmell(AbstractSmell smell) { + testSmells.add(smell); + } + + public void clear() { + for (InputStream inputStream : inputStreams) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + for (AbstractSmell smell : testSmells) { + smell.clear(); + } + testSmells.clear(); + } + + /** + * Factory method that provides a new instance of the TestSmellDetector + * + * @return new TestSmellDetector instance + */ + public static TestSmellDetector createTestSmellDetector() { + return new TestSmellDetector(); + } + + /** + * Provides the names of the smells that are being checked for in the code + * + * @return list of smell names + */ + public List getTestSmellNames() { + return testSmells.stream().map(AbstractSmell::getSmellName).collect(Collectors.toList()); + } + + /** + * Loads the java source code file into an AST and then analyzes it for the existence of the different types of test smells + */ + public void detectSmells(TestFile testFile) { + CompilationUnit testFileCompilationUnit = parseIntoCompilationUnit(testFile.getTestFilePath()); + + CompilationUnit productionFileCompilationUnit = parseIntoCompilationUnit(testFile.getProductionFilePath()); + + for (AbstractSmell smellPrototype : testSmells) { + AbstractSmell smell = smellPrototype.recreate(); + try { + smell.runAnalysis(testFileCompilationUnit, productionFileCompilationUnit, testFile.getTestFileNameWithoutExtension(), testFile.getProductionFileNameWithoutExtension()); + } catch (FileNotFoundException ignored) { + } + + testFile.addDetectedSmell(smell); + } + + } + + private CompilationUnit parseIntoCompilationUnit(String filePath) { + if (StringUtils.isEmpty(filePath)) { + return null; + } + InputStream testFileInputStream = null; + try { + testFileInputStream = new FileInputStream(filePath); + } catch (IOException e) { + testFileInputStream = getClass().getResourceAsStream(filePath); + } finally { + inputStreams.add(testFileInputStream); + } + assert Objects.nonNull(testFileInputStream); + return JavaParser.parse(testFileInputStream); + } +} diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/internal/Extractor.java b/src/main/java/edu/rit/se/testsmells/testsmell/internal/Extractor.java new file mode 100644 index 0000000..d98d902 --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/internal/Extractor.java @@ -0,0 +1,141 @@ +package edu.rit.se.testsmells.testsmell.internal; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.ExtractingStrategy; +import edu.rit.se.testsmells.testsmell.Report; +import edu.rit.se.testsmells.testsmell.SmellyElement; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class Extractor implements ExtractingStrategy { + + @Override + public List extract(List smells, Class type) { + return merge(smells.stream().flatMap(smell -> filterAndFlatten(type, smell)).collect(Collectors.toList())); + } + + private Stream filterAndFlatten(Class type, AbstractSmell smell) { + List elements = smell.getSmellyElements(); + String smellType = smell.getSmellName(); + return elements.stream().filter(type::isInstance).map(elem -> ReportCell.fromSmellyElement(elem, smellType)); + } + + private List merge(List elements) { + List names = listUniqueNames(elements); + return names.stream().map(name -> ReportOutput.fromCell(filterByName(elements, name))).collect(Collectors.toList()); + } + + private List listUniqueNames(List elements) { + return elements.stream().map(elem -> elem.name).distinct().collect(Collectors.toList()); + } + + private List filterByName(List elements, String name) { + return elements.stream().filter(elem -> elem.name.equals(name)).collect(Collectors.toList()); + } + + /** + * An intermediate representation of reporting data (private-only POJO) + * Intended to remind bi-dimensional (TestSmells x SmellyElement) matrix's cell. + */ + public static class ReportCell { + private String smellType; + private String name; + private Map data; + private boolean hasSmell; + + /** + * Static factory method to convert from hierarchical SmellyElement into a flat ReportCell representation + * + * @param elem The origin SmellyElement + * @param smellType The TestSmell's type name + * @return A valid ReportCell + */ + static ReportCell fromSmellyElement(SmellyElement elem, String smellType) { + ReportCell rc = new ReportCell(); + rc.smellType = smellType; + rc.name = elem.getElementName(); + rc.data = elem.getData(); + rc.hasSmell = elem.hasSmell(); + return rc; + } + } + + /** + * The final output model + */ + public static class ReportOutput implements Report { + private static final String NAME_KEY = "Element Name"; + private Map smellsPresence; + private Map data; + private String name; + private List values = null; + private List keys = null; + + /** + * Merge ReportCells into a ReportOutput + * + * @param cells ReportCells related to the same SmellyElement + * @return A ReportOutput with all data from cells merged + */ + static ReportOutput fromCell(List cells) { + ReportOutput output = new ReportOutput(); + + String elementName = cells.get(0).name; + assert cells.stream().allMatch(cell -> cell.name.equals(elementName)); + + output.name = elementName; + output.data = new HashMap<>(); + output.smellsPresence = new HashMap<>(); + + cells.forEach(cell -> { + // Merge all cells' data into the same output.data map + output.data.putAll(cell.data); + // Use logic-OR operation as hasSmell's aggregation function + boolean hasSmell = output.smellsPresence.getOrDefault(cell.smellType, false) || cell.hasSmell; + // Store each smell type in a map with a flag indicating whether it occurs or not + output.smellsPresence.put(cell.smellType, hasSmell); + }); + return output; + } + + @Override + public List getEntryValues() { + if (values == null) { + values = new ArrayList<>(); + values.add(name); + values.addAll(data.values()); + smellsPresence.values().forEach(e -> values.add(e.toString())); + } + return values; + } + + @Override + public List getEntryKeys() { + if (keys == null) { + keys = new ArrayList<>(); + keys.add(NAME_KEY); + keys.addAll(data.keySet()); + keys.addAll(smellsPresence.keySet()); + } + return keys; + } + + @Override + public String getValue(String key) { + if (key.equals(NAME_KEY)) { + return name; + } else if (data.containsKey(key)) { + return data.get(key); + } else if (smellsPresence.containsKey(key)) { + return smellsPresence.get(key).toString(); + } else { + return null; + } + } + } +} diff --git a/src/main/java/testsmell/smell/AssertionRoulette.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java similarity index 82% rename from src/main/java/testsmell/smell/AssertionRoulette.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java index ff196db..9524bf8 100644 --- a/src/main/java/testsmell/smell/AssertionRoulette.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java @@ -1,17 +1,13 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestMethod; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.List; /** * "Guess what's wrong?" This smell comes from having a number of assertions in a test method that have no explanation. @@ -20,27 +16,24 @@ */ public class AssertionRoulette extends AbstractSmell { - private List smellyElementList; private int assertionsCount = 0; + private CompilationUnit testFileCompilationUnit; public AssertionRoulette() { - smellyElementList = new ArrayList<>(); + super(); } - /** - * Checks of 'Assertion Roulette' smell - */ @Override - public String getSmellName() { - return "Assertion Roulette"; + public AbstractSmell recreate() { + return new AssertionRoulette(); } /** - * Returns true if any of the elements has a smell + * Checks of 'Assertion Roulette' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "Assertion Roulette"; } /** @@ -50,7 +43,8 @@ public boolean getHasSmell() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { AssertionRoulette.ClassVisitor classVisitor; classVisitor = new AssertionRoulette.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); assertionsCount = classVisitor.overallAssertions; } @@ -58,14 +52,6 @@ public int getAssertionsCount() { return assertionsCount; } - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; - } - private class ClassVisitor extends VoidVisitorAdapter { private MethodDeclaration currentMethod = null; @@ -77,9 +63,9 @@ private class ClassVisitor extends VoidVisitorAdapter { // examine all methods in the test class @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { currentMethod = n; - testMethod = new TestMethod(n.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(false); //default value is false (i.e. no smell) super.visit(n, arg); @@ -90,9 +76,10 @@ public void visit(MethodDeclaration n, Void arg) { else if (assertNoMessageCount >= 1) //if there is more than one assert statement, then all the asserts need to have an explanation message testMethod.setHasSmell(true); - testMethod.addDataItem("AssertCount", String.valueOf(assertNoMessageCount)); + testMethod.addDataItem("BadAssertCount", String.valueOf(assertNoMessageCount)); + testMethod.addDataItem("TotalAssertCount", String.valueOf(assertCount)); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; diff --git a/src/main/java/testsmell/smell/ConditionalTestLogic.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java similarity index 81% rename from src/main/java/testsmell/smell/ConditionalTestLogic.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java index 1279ac5..df5eb48 100644 --- a/src/main/java/testsmell/smell/ConditionalTestLogic.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java @@ -1,27 +1,29 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.ConditionalExpr; import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestMethod; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.List; /* This class check a test method for the existence of loops and conditional statements in the methods body */ public class ConditionalTestLogic extends AbstractSmell { - private List smellyElementList; + + private CompilationUnit testFileCompilationUnit; public ConditionalTestLogic() { - smellyElementList = new ArrayList<>(); + super(); + } + + @Override + public AbstractSmell recreate() { + return new ConditionalTestLogic(); } /** @@ -32,13 +34,6 @@ public String getSmellName() { return "Conditional Test Logic"; } - /** - * Returns true if any of the elements has a smell - */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; - } /** * Analyze the test file for test methods that use conditional statements @@ -47,18 +42,10 @@ public boolean getHasSmell() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { ConditionalTestLogic.ClassVisitor classVisitor; classVisitor = new ConditionalTestLogic.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; - } - - private class ClassVisitor extends VoidVisitorAdapter { private MethodDeclaration currentMethod = null; private int conditionCount, ifCount, switchCount, forCount, foreachCount, whileCount = 0; @@ -67,9 +54,9 @@ private class ClassVisitor extends VoidVisitorAdapter { // examine all methods in the test class @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { currentMethod = n; - testMethod = new TestMethod(n.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(false); //default value is false (i.e. no smell) super.visit(n, arg); @@ -82,7 +69,7 @@ public void visit(MethodDeclaration n, Void arg) { testMethod.addDataItem("ForCount", String.valueOf(forCount)); testMethod.addDataItem("WhileCount", String.valueOf(whileCount)); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; diff --git a/src/main/java/testsmell/smell/ConstructorInitialization.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java similarity index 56% rename from src/main/java/testsmell/smell/ConstructorInitialization.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java index c964fc9..08f284d 100644 --- a/src/main/java/testsmell/smell/ConstructorInitialization.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java @@ -1,13 +1,12 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.type.ClassOrInterfaceType; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestClass; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestClass; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -19,72 +18,57 @@ This class checks if the code file contains a Constructor. Ideally, the test sui If this code detects the existence of a constructor, it sets the class as smelly */ public class ConstructorInitialization extends AbstractSmell { - - private List smellyElementList; private String testFileName; + private CompilationUnit testFileCompilationUnit; public ConstructorInitialization() { - smellyElementList = new ArrayList<>(); + super(); } - /** - * Checks of 'Constructor Initialization' smell - */ @Override - public String getSmellName() { - return "Constructor Initialization"; + public AbstractSmell recreate() { + return new ConstructorInitialization(); } /** - * Returns true if any of the elements has a smell + * Checks of 'Constructor Initialization' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "Constructor Initialization"; } /** * Analyze the test file for Constructor Initialization smell */ @Override - public void runAnalysis(CompilationUnit testFileCompilationUnit,CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { + public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { this.testFileName = testFileName; ConstructorInitialization.ClassVisitor classVisitor; classVisitor = new ConstructorInitialization.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); - } - - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { TestClass testClass; - boolean constructorAllowed=false; + boolean constructorAllowed = false; @Override public void visit(ClassOrInterfaceDeclaration n, Void arg) { - for(int i=0;ic.getNameAsString().equals("ActivityInstrumentationTestCase2")); + testClass = new TestClass(getFullClassName(testFileCompilationUnit, n)); + testClass.setHasSmell(false); + addSmellyElement(testClass); super.visit(n, arg); } @Override public void visit(ConstructorDeclaration n, Void arg) { // This check is needed to handle java files that have multiple classes - if(n.getNameAsString().equals(testFileName)) { - if(!constructorAllowed) { - testClass = new TestClass(n.getNameAsString()); - testClass.setHasSmell(true); - smellyElementList.add(testClass); - } + if (n.getNameAsString().equals(testFileName)) { + testClass.setHasSmell(!constructorAllowed); } } } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DefaultTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DefaultTest.java new file mode 100644 index 0000000..b2e790b --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DefaultTest.java @@ -0,0 +1,55 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestClass; + +import java.io.FileNotFoundException; + +/* +By default Android Studio creates default test classes when a project is created. These classes are meant to serve as an example for developers when wring unit tests +This code marks the class as smelly if the class name corresponds to the name of the default test classes + */ +public class DefaultTest extends AbstractSmell { + + + private CompilationUnit testFileCompilationUnit; + + public DefaultTest() { + super(); + } + + @Override + public AbstractSmell recreate() { + return new DefaultTest(); + } + + /** + * Checks of 'Default Test' smell + */ + @Override + public String getSmellName() { + return "Default Test"; + } + + @Override + public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { + DefaultTest.ClassVisitor classVisitor; + classVisitor = new DefaultTest.ClassVisitor(); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); + } + + private class ClassVisitor extends VoidVisitorAdapter { + + @Override + public void visit(ClassOrInterfaceDeclaration n, Void arg) { + TestClass testClass = new TestClass(getFullClassName(testFileCompilationUnit, n)); + testClass.setHasSmell(n.getNameAsString().equals("ExampleUnitTest") || n.getNameAsString().equals("ExampleInstrumentedTest")); + addSmellyElement(testClass); + super.visit(n, arg); + } + } +} diff --git a/src/main/java/testsmell/smell/DependentTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java similarity index 70% rename from src/main/java/testsmell/smell/DependentTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java index 38fb2bd..014dff6 100644 --- a/src/main/java/testsmell/smell/DependentTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java @@ -1,12 +1,11 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -14,29 +13,26 @@ public class DependentTest extends AbstractSmell { - private List smellyElementList; + private List testMethods; public DependentTest() { - smellyElementList = new ArrayList<>(); + super(); testMethods = new ArrayList<>(); } - /** - * Checks of 'DependentTest' smell - */ @Override - public String getSmellName() { - return "Dependent Test"; + public AbstractSmell recreate() { + return new DependentTest(); } /** - * Returns true if any of the elements has a smell + * Checks of 'DependentTest' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "Dependent Test"; } /** @@ -49,27 +45,17 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit classVisitor.visit(testFileCompilationUnit, null); for (TestMethod testMethod : testMethods) { - if (testMethod.getCalledMethods().stream().anyMatch(x -> x.getName().equals(testMethods.stream().map(z -> z.getMethodDeclaration().getNameAsString())))){ - smellyElementList.add(new testsmell.TestMethod(testMethod.getMethodDeclaration().getNameAsString())); - } - } - -/* - for (int i = 0; i < testMethods.get(i).getCalledMethods().size(); i++) { - for (TestMethod testMethod : testMethods) { - if (testMethods.get(i).getCalledMethods().stream().anyMatch(x -> x.getName().equals(testMethod.getMethodDeclaration().getNameAsString()))) { - smellyElementList.add(new testsmell.TestMethod(testMethod.getMethodDeclaration().getNameAsString())); + edu.rit.se.testsmells.testsmell.TestMethod smell = new edu.rit.se.testsmells.testsmell.TestMethod(getFullMethodName(testFileCompilationUnit, testMethod.getMethodDeclaration())); + smell.setHasSmell(false); + for (TestMethod otherTestMethod : testMethods) { + if (testMethod != otherTestMethod) { + if (testMethod.getCalledMethods().stream().anyMatch(x -> x.getName().equals(otherTestMethod.getMethodDeclaration().getNameAsString()))) { + smell.setHasSmell(true); + } } } - }*/ - } - - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; + addSmellyElement(smell); + } } private class ClassVisitor extends VoidVisitorAdapter { @@ -79,7 +65,7 @@ private class ClassVisitor extends VoidVisitorAdapter { // examine all methods in the test class @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { currentMethod = n; calledMethods = new ArrayList<>(); @@ -101,6 +87,14 @@ public void visit(MethodCallExpr n, Void arg) { } private class TestMethod { + private List calledMethods; + private MethodDeclaration methodDeclaration; + + public TestMethod(MethodDeclaration methodDeclaration, List calledMethods) { + this.methodDeclaration = methodDeclaration; + this.calledMethods = calledMethods; + } + public List getCalledMethods() { return calledMethods; } @@ -108,17 +102,17 @@ public List getCalledMethods() { public MethodDeclaration getMethodDeclaration() { return methodDeclaration; } - - public TestMethod(MethodDeclaration methodDeclaration, List calledMethods) { - this.methodDeclaration = methodDeclaration; - this.calledMethods = calledMethods; - } - - private List calledMethods; - private MethodDeclaration methodDeclaration; } private class CalledMethod { + private int totalArguments; + private String name; + + public CalledMethod(int totalArguments, String name) { + this.totalArguments = totalArguments; + this.name = name; + } + public int getTotalArguments() { return totalArguments; } @@ -126,13 +120,5 @@ public int getTotalArguments() { public String getName() { return name; } - - public CalledMethod(int totalArguments, String name) { - this.totalArguments = totalArguments; - this.name = name; - } - - private int totalArguments; - private String name; } } diff --git a/src/main/java/testsmell/smell/DuplicateAssert.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java similarity index 81% rename from src/main/java/testsmell/smell/DuplicateAssert.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java index fcc6037..4a29642 100644 --- a/src/main/java/testsmell/smell/DuplicateAssert.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java @@ -1,39 +1,38 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestMethod; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class DuplicateAssert extends AbstractSmell { - private List smellyElementList; + + private CompilationUnit testFileCompilationUnit; public DuplicateAssert() { - smellyElementList = new ArrayList<>(); + super(); } - /** - * Checks of 'Duplicate Assert' smell - */ @Override - public String getSmellName() { - return "Duplicate Assert"; + public AbstractSmell recreate() { + return new DuplicateAssert(); } /** - * Returns true if any of the elements has a smell + * Checks of 'Duplicate Assert' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "Duplicate Assert"; } /** @@ -43,15 +42,8 @@ public boolean getHasSmell() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { DuplicateAssert.ClassVisitor classVisitor; classVisitor = new DuplicateAssert.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); - } - - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } @@ -64,25 +56,25 @@ private class ClassVisitor extends VoidVisitorAdapter { // examine all methods in the test class @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { currentMethod = n; - testMethod = new TestMethod(n.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(false); //default value is false (i.e. no smell) super.visit(n, arg); // if there are duplicate messages, then the smell exists - Set set1 = new HashSet(assertMessage); + Set set1 = new HashSet<>(assertMessage); if (set1.size() < assertMessage.size()) { testMethod.setHasSmell(true); } // if there are duplicate assert methods, then the smell exists - Set set2 = new HashSet(assertMethod); + Set set2 = new HashSet<>(assertMethod); if (set2.size() < assertMethod.size()) { testMethod.setHasSmell(true); } - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; diff --git a/src/main/java/testsmell/smell/EagerTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java similarity index 79% rename from src/main/java/testsmell/smell/EagerTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java index 7412616..bdad57f 100644 --- a/src/main/java/testsmell/smell/EagerTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java @@ -1,4 +1,4 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Modifier; @@ -8,9 +8,9 @@ import com.github.javaparser.ast.body.VariableDeclarator; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.NameExpr; -import com.github.javaparser.ast.expr.VariableDeclarationExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.*; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -22,29 +22,27 @@ public class EagerTest extends AbstractSmell { private static final String TEST_FILE = "Test"; private static final String PRODUCTION_FILE = "Production"; private String productionClassName; - private List smellyElementList; + private List productionMethods; private int eagerCount; + private CompilationUnit testFileCompilationUnit; public EagerTest() { + super(); productionMethods = new ArrayList<>(); - smellyElementList = new ArrayList<>(); } - /** - * Checks of 'Eager Test' smell - */ @Override - public String getSmellName() { - return "Eager Test"; + public AbstractSmell recreate() { + return new EagerTest(); } /** - * Returns true if any of the elements has a smell + * Checks of 'Eager Test' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "Eager Test"; } /** @@ -62,18 +60,11 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit classVisitor.visit(productionFileCompilationUnit, null); classVisitor = new EagerTest.ClassVisitor(TEST_FILE); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); eagerCount = classVisitor.overallEager; } - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; - } - public int getEagerCount() { return eagerCount; } @@ -117,14 +108,14 @@ public void visit(EnumDeclaration n, Void arg) { public void visit(MethodDeclaration n, Void arg) { // ensure that this method is only executed for the test file if (Objects.equals(fileType, TEST_FILE)) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { currentMethod = n; - testMethod = new TestMethod(currentMethod.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, currentMethod)); testMethod.setHasSmell(false); //default value is false (i.e. no smell) super.visit(n, arg); testMethod.setHasSmell(eagerCount > 1); //the method has a smell if there is more than 1 call to production methods - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; @@ -164,8 +155,7 @@ public void visit(MethodCallExpr n, Void arg) { calledMethods.add(n.getNameAsString()); } else { if (n.getScope().isPresent()) { - //this if statement checks if the method is chained and gets the final scope - if ((n.getScope().get() instanceof MethodCallExpr)) { + if (isMethodChained(n)) { getFinalScope(n); nameExpr = tempNameExpr; } @@ -177,7 +167,7 @@ public void visit(MethodCallExpr n, Void arg) { //checks if the scope of the method being called is either of production class (e.g. static method) //or ///if the scope matches a variable which, in turn, is of type of the production class - if (nameExpr.getNameAsString().equals(productionClassName) || + if (productionClassName.equals(nameExpr.getNameAsString()) || productionVariables.contains(nameExpr.getNameAsString())) { if (!calledMethods.contains(n.getNameAsString())) { eagerCount++; @@ -192,6 +182,10 @@ public void visit(MethodCallExpr n, Void arg) { super.visit(n, arg); } + private boolean isMethodChained(MethodCallExpr n) { + return n.getScope().get() instanceof MethodCallExpr; + } + private NameExpr tempNameExpr; /** @@ -199,7 +193,7 @@ public void visit(MethodCallExpr n, Void arg) { */ private void getFinalScope(MethodCallExpr n) { if (n.getScope().isPresent()) { - if ((n.getScope().get() instanceof MethodCallExpr)) { + if ((isMethodChained(n))) { getFinalScope((MethodCallExpr) n.getScope().get()); } else if ((n.getScope().get() instanceof NameExpr)) { tempNameExpr = ((NameExpr) n.getScope().get()); @@ -207,22 +201,6 @@ private void getFinalScope(MethodCallExpr n) { } } -// /** -// * The purpose of this method is to capture the names of all variables, declared in the method body, that are of type of the production class. -// * The variable is captured as and when the code statement is parsed/evaluated by the parser -// */ -// @Override -// public void visit(VariableDeclarationExpr n, Void arg) { -// if (currentMethod != null) { -// for (int i = 0; i < n.getVariables().size(); i++) { -// if (productionClassName.equals(n.getVariable(i).getType().asString())) { -// productionVariables.add(n.getVariable(i).getNameAsString()); -// } -// } -// } -// super.visit(n, arg); -// } - @Override public void visit(VariableDeclarator n, Void arg) { if (Objects.equals(fileType, TEST_FILE)) { diff --git a/src/main/java/testsmell/smell/EmptyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java similarity index 68% rename from src/main/java/testsmell/smell/EmptyTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java index 4bc18ec..5c3dc6f 100644 --- a/src/main/java/testsmell/smell/EmptyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java @@ -1,16 +1,12 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestMethod; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.List; /** * This class checks if a test method is empty (i.e. the method does not contain statements in its body) @@ -18,26 +14,24 @@ */ public class EmptyTest extends AbstractSmell { - private List smellyElementList; + + private CompilationUnit testFileCompilationUnit; public EmptyTest() { - smellyElementList = new ArrayList<>(); + super(); } - /** - * Checks of 'Empty Test' smell - */ @Override - public String getSmellName() { - return "EmptyTest"; + public AbstractSmell recreate() { + return new EmptyTest(); } /** - * Returns true if any of the elements has a smell + * Checks of 'Empty Test' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "EmptyTest"; } /** @@ -47,15 +41,8 @@ public boolean getHasSmell() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { EmptyTest.ClassVisitor classVisitor; classVisitor = new EmptyTest.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); - } - - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } /** @@ -69,8 +56,8 @@ private class ClassVisitor extends VoidVisitorAdapter { */ @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n)) { - testMethod = new TestMethod(n.getNameAsString()); + if (isValidTestMethod(n)) { + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(false); //default value is false (i.e. no smell) //method should not be abstract if (!n.isAbstract()) { @@ -81,7 +68,7 @@ public void visit(MethodDeclaration n, Void arg) { } } } - smellyElementList.add(testMethod); + addSmellyElement(testMethod); } } } diff --git a/src/main/java/testsmell/smell/ExceptionCatchingThrowing.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java similarity index 74% rename from src/main/java/testsmell/smell/ExceptionCatchingThrowing.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java index 50cc5e0..61079bc 100644 --- a/src/main/java/testsmell/smell/ExceptionCatchingThrowing.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java @@ -1,18 +1,14 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.stmt.CatchClause; import com.github.javaparser.ast.stmt.ThrowStmt; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestMethod; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.List; /* This class checks if test methods in the class either catch or throw exceptions. Use Junit's exception handling to automatically pass/fail the test @@ -20,26 +16,24 @@ */ public class ExceptionCatchingThrowing extends AbstractSmell { - private List smellyElementList; + + private CompilationUnit testFileCompilationUnit; public ExceptionCatchingThrowing() { - smellyElementList = new ArrayList<>(); + super(); } - /** - * Checks of 'Exception Catching Throwing' smell - */ @Override - public String getSmellName() { - return "Exception Catching Throwing"; + public AbstractSmell recreate() { + return new ExceptionCatchingThrowing(); } /** - * Returns true if any of the elements has a smell + * Checks of 'Exception Catching Throwing' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "Exception Catching Throwing"; } /** @@ -49,15 +43,8 @@ public boolean getHasSmell() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { ExceptionCatchingThrowing.ClassVisitor classVisitor; classVisitor = new ExceptionCatchingThrowing.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); - } - - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -69,9 +56,9 @@ private class ClassVisitor extends VoidVisitorAdapter { // examine all methods in the test class @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { currentMethod = n; - testMethod = new TestMethod(n.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(false); //default value is false (i.e. no smell) super.visit(n, arg); @@ -81,7 +68,7 @@ public void visit(MethodDeclaration n, Void arg) { testMethod.setHasSmell(exceptionCount >= 1); testMethod.addDataItem("ExceptionCount", String.valueOf(exceptionCount)); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; diff --git a/src/main/java/testsmell/smell/GeneralFixture.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java similarity index 72% rename from src/main/java/testsmell/smell/GeneralFixture.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java index 9d5ddf5..df07f89 100644 --- a/src/main/java/testsmell/smell/GeneralFixture.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java @@ -1,4 +1,4 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.NodeList; @@ -11,50 +11,47 @@ import com.github.javaparser.ast.stmt.BlockStmt; import com.github.javaparser.ast.stmt.ExpressionStmt; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestMethod; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; import java.util.*; public class GeneralFixture extends AbstractSmell { - private List smellyElementList; + List methodList; MethodDeclaration setupMethod; List fieldList; List setupFields; + private CompilationUnit testFileCompilationUnit; public GeneralFixture() { - smellyElementList = new ArrayList<>(); + super(); methodList = new ArrayList<>(); fieldList = new ArrayList<>(); setupFields = new ArrayList<>(); } - /** - * Checks of 'General Fixture' smell - */ @Override - public String getSmellName() { - return "General Fixture"; + public AbstractSmell recreate() { + return new GeneralFixture(); } /** - * Returns true if any of the elements has a smell + * Checks of 'General Fixture' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "General Fixture"; } @Override public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { GeneralFixture.ClassVisitor classVisitor; classVisitor = new GeneralFixture.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); //This call will populate the list of test methods and identify the setup method [visit(ClassOrInterfaceDeclaration n)] + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); //This call will populate the list of test methods and identify the setup method [visit(ClassOrInterfaceDeclaration n)] //Proceed with general fixture analysis if setup method exists if (setupMethod != null) { @@ -86,35 +83,26 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit } } - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; - } - private class ClassVisitor extends VoidVisitorAdapter { - private MethodDeclaration methodDeclaration = null; private MethodDeclaration currentMethod = null; TestMethod testMethod; - private Set fixtureCount = new HashSet(); + private Set fixtureCount = new HashSet<>(); @Override public void visit(ClassOrInterfaceDeclaration n, Void arg) { NodeList> members = n.getMembers(); - for (int i = 0; i < members.size(); i++) { - if (members.get(i) instanceof MethodDeclaration) { - methodDeclaration = (MethodDeclaration) members.get(i); + for (BodyDeclaration member : members) { + if (member instanceof MethodDeclaration) { + MethodDeclaration methodDeclaration = (MethodDeclaration) member; //Get a list of all test methods - if (Util.isValidTestMethod(methodDeclaration)) { + if (isValidTestMethod(methodDeclaration)) { methodList.add(methodDeclaration); } //Get the setup method - if (Util.isValidSetupMethod(methodDeclaration)) { + if (isValidSetupMethod(methodDeclaration)) { //It should have a body if (methodDeclaration.getBody().isPresent()) { setupMethod = methodDeclaration; @@ -123,8 +111,8 @@ public void visit(ClassOrInterfaceDeclaration n, Void arg) { } //Get all fields in the class - if (members.get(i) instanceof FieldDeclaration) { - fieldList.add((FieldDeclaration) members.get(i)); + if (member instanceof FieldDeclaration) { + fieldList.add((FieldDeclaration) member); } } } @@ -132,17 +120,17 @@ public void visit(ClassOrInterfaceDeclaration n, Void arg) { // examine all methods in the test class @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { currentMethod = n; //call visit(NameExpr) for current method super.visit(n, arg); - testMethod = new TestMethod(n.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(fixtureCount.size() != setupFields.size()); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); - fixtureCount = new HashSet();; + fixtureCount = new HashSet<>(); currentMethod = null; } } @@ -152,10 +140,7 @@ public void visit(NameExpr n, Void arg) { if (currentMethod != null) { //check if the variable contained in the current test method is also contained in the setup method if (setupFields.contains(n.getNameAsString())) { - if(!fixtureCount.contains(n.getNameAsString())){ - fixtureCount.add(n.getNameAsString()); - } - //System.out.println(currentMethod.getNameAsString() + " : " + n.getName().toString()); + fixtureCount.add(n.getNameAsString()); } } diff --git a/src/main/java/testsmell/smell/IgnoredTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java similarity index 50% rename from src/main/java/testsmell/smell/IgnoredTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java index 275bdbd..002361d 100644 --- a/src/main/java/testsmell/smell/IgnoredTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java @@ -1,41 +1,37 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Modifier; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestClass; -import testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestClass; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.List; public class IgnoredTest extends AbstractSmell { - private List smellyElementList; + + private CompilationUnit testFileCompilationUnit; public IgnoredTest() { - smellyElementList = new ArrayList<>(); + super(); } - /** - * Checks of 'Ignored Test' smell - */ @Override - public String getSmellName() { - return "IgnoredTest"; + public AbstractSmell recreate() { + return new IgnoredTest(); } /** - * Returns true if any of the elements has a smell + * Checks of 'Ignored Test' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "IgnoredTest"; } /** @@ -45,17 +41,13 @@ public boolean getHasSmell() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { IgnoredTest.ClassVisitor classVisitor; classVisitor = new IgnoredTest.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; + private boolean isIgnored(NodeWithAnnotations n) { + return n.getAnnotationByName("Ignore").isPresent() || n.getAnnotationByName("Disabled").isPresent(); } - /** * Visitor class */ @@ -68,11 +60,9 @@ private class ClassVisitor extends VoidVisitorAdapter { */ @Override public void visit(ClassOrInterfaceDeclaration n, Void arg) { - if (n.getAnnotationByName("Ignore").isPresent()) { - testClass = new TestClass(n.getNameAsString()); - testClass.setHasSmell(true); - smellyElementList.add(testClass); - } + testClass = new TestClass(getFullClassName(testFileCompilationUnit, n)); + testClass.setHasSmell(isIgnored(n)); + addSmellyElement(testClass); super.visit(n, arg); } @@ -81,29 +71,19 @@ public void visit(ClassOrInterfaceDeclaration n, Void arg) { */ @Override public void visit(MethodDeclaration n, Void arg) { - + //JUnit 5 + //check if test method has Disabled annotation //JUnit 4 //check if test method has Ignore annotation - if (n.getAnnotationByName("Test").isPresent()) { - if (n.getAnnotationByName("Ignore").isPresent()) { - testMethod = new TestMethod(n.getNameAsString()); - testMethod.setHasSmell(true); - smellyElementList.add(testMethod); - return; - } - } - //JUnit 3 //check if test method is not public - if (n.getNameAsString().toLowerCase().startsWith("test")) { - if (!n.getModifiers().contains(Modifier.PUBLIC)) { - testMethod = new TestMethod(n.getNameAsString()); - testMethod.setHasSmell(true); - smellyElementList.add(testMethod); - return; - } + if (n.getAnnotationByName("Test").isPresent() || n.getNameAsString().toLowerCase().startsWith("test")) { + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); + testMethod.setHasSmell(isIgnored(n) || !n.getModifiers().contains(Modifier.PUBLIC)); + addSmellyElement(testMethod); } } + } } diff --git a/src/main/java/testsmell/smell/LazyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java similarity index 88% rename from src/main/java/testsmell/smell/LazyTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java index 55efa39..2c0a9ca 100644 --- a/src/main/java/testsmell/smell/LazyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java @@ -1,4 +1,4 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Modifier; @@ -8,12 +8,9 @@ import com.github.javaparser.ast.body.VariableDeclarator; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.NameExpr; -import com.github.javaparser.ast.expr.VariableDeclarationExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestMethod; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -25,30 +22,28 @@ public class LazyTest extends AbstractSmell { private static final String TEST_FILE = "Test"; private static final String PRODUCTION_FILE = "Production"; private String productionClassName; - private List smellyElementList; + private List calledProductionMethods; private List productionMethods; + private CompilationUnit testFileCompilationUnit; public LazyTest() { + super(); productionMethods = new ArrayList<>(); - smellyElementList = new ArrayList<>(); calledProductionMethods = new ArrayList<>(); } - /** - * Checks of 'Lazy Test' smell - */ @Override - public String getSmellName() { - return "Lazy Test"; + public AbstractSmell recreate() { + return new LazyTest(); } /** - * Returns true if any of the elements has a smell + * Checks of 'Lazy Test' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "Lazy Test"; } /** @@ -66,7 +61,8 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit classVisitor.visit(productionFileCompilationUnit, null); classVisitor = new LazyTest.ClassVisitor(TEST_FILE); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); for (MethodUsage method : calledProductionMethods) { List s = calledProductionMethods.stream().filter(x -> x.getProductionMethod().equals(method.getProductionMethod())).collect(Collectors.toList()); @@ -76,20 +72,12 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit // If the counts were equal it means that the production method is only used (called from) inside one test method TestMethod testClass = new TestMethod(method.getTestMethod()); testClass.setHasSmell(true); - smellyElementList.add(testClass); + addSmellyElement(testClass); } } } } - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; - } - private class MethodUsage { private String testMethod, productionMethod; @@ -143,9 +131,9 @@ public void visit(EnumDeclaration n, Void arg) { public void visit(MethodDeclaration n, Void arg) { // ensure that this method is only executed for the test file if (Objects.equals(fileType, TEST_FILE)) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { currentMethod = n; - testMethod = new TestMethod(currentMethod.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, currentMethod)); testMethod.setHasSmell(false); //default value is false (i.e. no smell) super.visit(n, arg); @@ -180,7 +168,7 @@ public void visit(MethodCallExpr n, Void arg) { if (currentMethod != null) { if (productionMethods.stream().anyMatch(i -> i.getNameAsString().equals(n.getNameAsString()) && i.getParameters().size() == n.getArguments().size())) { - calledProductionMethods.add(new MethodUsage(currentMethod.getNameAsString(), n.getNameAsString())); + calledProductionMethods.add(new MethodUsage(getFullMethodName(testFileCompilationUnit, currentMethod), n.getNameAsString())); // TODO: Should production method name also be in full form? } else { if (n.getScope().isPresent()) { if (n.getScope().get() instanceof NameExpr) { @@ -189,7 +177,7 @@ public void visit(MethodCallExpr n, Void arg) { ///if the scope matches a variable which, in turn, is of type of the production class if (((NameExpr) n.getScope().get()).getNameAsString().equals(productionClassName) || productionVariables.contains(((NameExpr) n.getScope().get()).getNameAsString())) { - calledProductionMethods.add(new MethodUsage(currentMethod.getNameAsString(), n.getNameAsString())); + calledProductionMethods.add(new MethodUsage(getFullMethodName(testFileCompilationUnit, currentMethod), n.getNameAsString())); // TODO: Should production method name also be in full form? } } } diff --git a/src/main/java/testsmell/smell/MagicNumberTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java similarity index 66% rename from src/main/java/testsmell/smell/MagicNumberTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java index 4643795..15615540 100644 --- a/src/main/java/testsmell/smell/MagicNumberTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java @@ -1,4 +1,4 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; @@ -6,37 +6,31 @@ import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.ObjectCreationExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestMethod; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.List; -public class MagicNumberTest extends AbstractSmell { +public class MagicNumberTest extends AbstractSmell { - private List smellyElementList; + + private CompilationUnit testFileCompilationUnit; public MagicNumberTest() { - smellyElementList = new ArrayList<>(); + super(); } - /** - * Checks of 'MagicNumberTest' smell - */ @Override - public String getSmellName() { - return "Magic Number Test"; + public AbstractSmell recreate() { + return new MagicNumberTest(); } /** - * Returns true if any of the elements has a smell + * Checks of 'MagicNumberTest' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "Magic Number Test"; } /** @@ -46,15 +40,8 @@ public boolean getHasSmell() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { MagicNumberTest.ClassVisitor classVisitor; classVisitor = new MagicNumberTest.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); - } - - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -65,16 +52,16 @@ private class ClassVisitor extends VoidVisitorAdapter { // examine all methods in the test class @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { currentMethod = n; - testMethod = new TestMethod(n.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(false); //default value is false (i.e. no smell) super.visit(n, arg); testMethod.setHasSmell(magicCount >= 1); testMethod.addDataItem("MagicNumberCount", String.valueOf(magicCount)); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; @@ -98,21 +85,21 @@ public void visit(MethodCallExpr n, Void arg) { // checks all arguments of the assert method for (Expression argument:n.getArguments()) { // if the argument is a number - if(Util.isNumber(argument.toString())){ - magicCount++; - } - // if the argument contains an ObjectCreationExpr (e.g. assertEquals(new Integer(2),...) - else if(argument instanceof ObjectCreationExpr){ - for (Expression objectArguments:((ObjectCreationExpr) argument).getArguments()){ - if(Util.isNumber(objectArguments.toString())){ - magicCount++; - } - } - } + if (isNumber(argument.toString())) { + magicCount++; + } + // if the argument contains an ObjectCreationExpr (e.g. assertEquals(new Integer(2),...) + else if (argument instanceof ObjectCreationExpr) { + for (Expression objectArguments : ((ObjectCreationExpr) argument).getArguments()) { + if (isNumber(objectArguments.toString())) { + magicCount++; + } + } + } // if the argument contains an MethodCallExpr (e.g. assertEquals(someMethod(2),...) else if(argument instanceof MethodCallExpr){ for (Expression objectArguments:((MethodCallExpr) argument).getArguments()){ - if(Util.isNumber(objectArguments.toString())){ + if (isNumber(objectArguments.toString())) { magicCount++; } } diff --git a/src/main/java/testsmell/smell/MysteryGuest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java similarity index 56% rename from src/main/java/testsmell/smell/MysteryGuest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java index 6aa1dc8..87a0733 100644 --- a/src/main/java/testsmell/smell/MysteryGuest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java @@ -1,14 +1,12 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.AnnotationExpr; import com.github.javaparser.ast.expr.VariableDeclarationExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestMethod; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -24,26 +22,24 @@ */ public class MysteryGuest extends AbstractSmell { - private List smellyElementList; + private static final boolean doClassLevelVariableCheck = false; + private CompilationUnit testFileCompilationUnit; public MysteryGuest() { - smellyElementList = new ArrayList<>(); + super(); } - /** - * Checks of 'Mystery Guest' smell - */ @Override - public String getSmellName() { - return "Mystery Guest"; + public AbstractSmell recreate() { + return new MysteryGuest(); } /** - * Returns true if any of the elements has a smell + * Checks of 'Mystery Guest' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "Mystery Guest"; } /** @@ -53,15 +49,8 @@ public boolean getHasSmell() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { MysteryGuest.ClassVisitor classVisitor; classVisitor = new MysteryGuest.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); - } - - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -80,31 +69,6 @@ private class ClassVisitor extends VoidVisitorAdapter { "SoapObject" )); - /* - private List databaseMethods = new ArrayList<>( - Arrays.asList( - "getWritableDatabase", - "getReadableDatabase", - "execSQL", - "rawQuery" - )); - private List fileMethods = new ArrayList<>( - Arrays.asList( - "getFilesDir", - "getDir", - "getCacheDir", - "createTempFile", - "getExternalStorageState", - "getExternalStoragePublicDirectory", - "getExternalFilesDir", - "getExternalCacheDir", - "getFreeSpace", - "getTotalSpace", - "deleteFile", - "fileList", - "openFileOutput", - "openRawResource")); - */ private MethodDeclaration currentMethod = null; private int mysteryCount = 0; TestMethod testMethod; @@ -112,16 +76,16 @@ private class ClassVisitor extends VoidVisitorAdapter { // examine all methods in the test class @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { currentMethod = n; - testMethod = new TestMethod(n.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(false); //default value is false (i.e. no smell) super.visit(n, arg); testMethod.setHasSmell(mysteryCount > 0); testMethod.addDataItem("MysteryCount", String.valueOf(mysteryCount)); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; @@ -129,33 +93,10 @@ public void visit(MethodDeclaration n, Void arg) { } } - /* - // examine the methods being called within the test method - @Override - public void visit(MethodCallExpr n, Void arg) { - super.visit(n, arg); - if (currentMethod != null){ - for (String methodName: fileMethods) { - if(n.getNameAsString().equals(methodName)){ - mysteryCount++; - } - } - for (String methodName: databaseMethods) { - if(n.getNameAsString().equals(methodName)){ - mysteryCount++; - } - } - } - } - */ - @Override public void visit(VariableDeclarationExpr n, Void arg) { super.visit(n, arg); - //Note: the null check limits the identification of variable types declared within the method body. - // Removing it will check for variables declared at the class level. - //TODO: to null check or not to null check??? - if (currentMethod != null) { + if (doClassLevelVariableCheck || currentMethod != null) { for (String variableType : mysteryTypes) { //check if the type variable encountered is part of the mystery type collection if ((n.getVariable(0).getType().asString().equals(variableType))) { diff --git a/src/main/java/testsmell/smell/PrintStatement.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java similarity index 79% rename from src/main/java/testsmell/smell/PrintStatement.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java index 3bcf3d0..c5e8af2 100644 --- a/src/main/java/testsmell/smell/PrintStatement.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java @@ -1,4 +1,4 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; @@ -6,14 +6,10 @@ import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestMethod; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.List; /* Test methods should not contain print statements as execution of unit tests is an automated process with little to no human intervention. Hence, print statements are redundant. @@ -21,26 +17,24 @@ This code checks the body of each test method if System.out. print(), println(), */ public class PrintStatement extends AbstractSmell { - private List smellyElementList; + + private CompilationUnit testFileCompilationUnit; public PrintStatement() { - smellyElementList = new ArrayList<>(); + super(); } - /** - * Checks of 'Print Statement' smell - */ @Override - public String getSmellName() { - return "Print Statement"; + public AbstractSmell recreate() { + return new PrintStatement(); } /** - * Returns true if any of the elements has a smell + * Checks of 'Print Statement' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "Print Statement"; } /** @@ -50,15 +44,8 @@ public boolean getHasSmell() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { PrintStatement.ClassVisitor classVisitor; classVisitor = new PrintStatement.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); - } - - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -69,16 +56,16 @@ private class ClassVisitor extends VoidVisitorAdapter { // examine all methods in the test class @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { currentMethod = n; - testMethod = new TestMethod(n.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(false); //default value is false (i.e. no smell) super.visit(n, arg); testMethod.setHasSmell(printCount >= 1); testMethod.addDataItem("PrintCount", String.valueOf(printCount)); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; diff --git a/src/main/java/testsmell/smell/RedundantAssertion.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java similarity index 85% rename from src/main/java/testsmell/smell/RedundantAssertion.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java index 47431d5..baed054 100644 --- a/src/main/java/testsmell/smell/RedundantAssertion.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java @@ -1,4 +1,4 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; @@ -6,40 +6,34 @@ import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.NullLiteralExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestMethod; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.List; /* If a test method contains an assert statement that explicitly returns a true or false, the method is marked as smelly */ public class RedundantAssertion extends AbstractSmell { - private List smellyElementList; + + private CompilationUnit testFileCompilationUnit; public RedundantAssertion() { - smellyElementList = new ArrayList<>(); + super(); } - /** - * Checks of 'Redundant Assertion' smell - */ @Override - public String getSmellName() { - return "Redundant Assertion"; + public AbstractSmell recreate() { + return new RedundantAssertion(); } /** - * Returns true if any of the elements has a smell + * Checks of 'Redundant Assertion' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "Redundant Assertion"; } /** @@ -49,15 +43,8 @@ public boolean getHasSmell() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { RedundantAssertion.ClassVisitor classVisitor; classVisitor = new RedundantAssertion.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); - } - - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -68,16 +55,16 @@ private class ClassVisitor extends VoidVisitorAdapter { // examine all methods in the test class @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { currentMethod = n; - testMethod = new TestMethod(n.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(false); //default value is false (i.e. no smell) super.visit(n, arg); testMethod.setHasSmell(redundantCount >= 1); testMethod.addDataItem("RedundantCount", String.valueOf(redundantCount)); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; diff --git a/src/main/java/testsmell/smell/ResourceOptimism.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java similarity index 83% rename from src/main/java/testsmell/smell/ResourceOptimism.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java index 26e5efb..20a47b0 100644 --- a/src/main/java/testsmell/smell/ResourceOptimism.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java @@ -1,15 +1,16 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.FieldDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.VariableDeclarator; -import com.github.javaparser.ast.expr.*; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.expr.NameExpr; +import com.github.javaparser.ast.expr.ObjectCreationExpr; +import com.github.javaparser.ast.expr.VariableDeclarationExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestMethod; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -17,26 +18,24 @@ public class ResourceOptimism extends AbstractSmell { - private List smellyElementList; + + private CompilationUnit testFileCompilationUnit; public ResourceOptimism() { - smellyElementList = new ArrayList<>(); + super(); } - /** - * Checks of 'Resource Optimism' smell - */ @Override - public String getSmellName() { - return "Resource Optimism"; + public AbstractSmell recreate() { + return new ResourceOptimism(); } /** - * Returns true if any of the elements has a smell + * Checks of 'Resource Optimism' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "Resource Optimism"; } /** @@ -46,15 +45,8 @@ public boolean getHasSmell() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { ResourceOptimism.ClassVisitor classVisitor; classVisitor = new ResourceOptimism.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); - } - - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } @@ -70,16 +62,16 @@ private class ClassVisitor extends VoidVisitorAdapter { // examine all methods in the test class @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n) || Util.isValidSetupMethod(n)) { + if (isValidTestMethod(n) || isValidSetupMethod(n)) { currentMethod = n; - testMethod = new TestMethod(n.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(false); //default value is false (i.e. no smell) super.visit(n, arg); - testMethod.setHasSmell(methodVariables.size() >= 1 || hasSmell==true); + testMethod.setHasSmell(methodVariables.size() >= 1 || hasSmell == true); testMethod.addDataItem("ResourceOptimismCount", String.valueOf(resourceOptimismCount)); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; diff --git a/src/main/java/testsmell/smell/SensitiveEquality.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java similarity index 76% rename from src/main/java/testsmell/smell/SensitiveEquality.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java index 726df94..540c9a1 100644 --- a/src/main/java/testsmell/smell/SensitiveEquality.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java @@ -1,41 +1,35 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestMethod; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.List; public class SensitiveEquality extends AbstractSmell { - private List smellyElementList; + + private CompilationUnit testFileCompilationUnit; public SensitiveEquality() { - smellyElementList = new ArrayList<>(); + super(); } - /** - * Checks of 'Sensitive Equality' smell - */ @Override - public String getSmellName() { - return "Sensitive Equality"; + public AbstractSmell recreate() { + return new SensitiveEquality(); } /** - * Returns true if any of the elements has a smell + * Checks of 'Sensitive Equality' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "Sensitive Equality"; } /** @@ -45,15 +39,8 @@ public boolean getHasSmell() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { SensitiveEquality.ClassVisitor classVisitor; classVisitor = new SensitiveEquality.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); - } - - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -64,16 +51,16 @@ private class ClassVisitor extends VoidVisitorAdapter { // examine all methods in the test class @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { currentMethod = n; - testMethod = new TestMethod(n.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(false); //default value is false (i.e. no smell) super.visit(n, arg); testMethod.setHasSmell(sensitiveCount >= 1); testMethod.addDataItem("SensitiveCount", String.valueOf(sensitiveCount)); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; diff --git a/src/main/java/testsmell/smell/SleepyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java similarity index 75% rename from src/main/java/testsmell/smell/SleepyTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java index bd397b3..cc5a44b 100644 --- a/src/main/java/testsmell/smell/SleepyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java @@ -1,18 +1,14 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestMethod; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.List; /* Use of Thread.sleep() in test methods can possibly lead to unexpected results as the processing time of tasks on different devices/machines can be different. Use mock objects instead @@ -20,26 +16,24 @@ */ public class SleepyTest extends AbstractSmell { - private List smellyElementList; + + private CompilationUnit testFileCompilationUnit; public SleepyTest() { - smellyElementList = new ArrayList<>(); + super(); } - /** - * Checks of 'SleepyTest' smell - */ @Override - public String getSmellName() { - return "Sleepy Test"; + public AbstractSmell recreate() { + return new SleepyTest(); } /** - * Returns true if any of the elements has a smell + * Checks of 'SleepyTest' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "Sleepy Test"; } /** @@ -49,15 +43,8 @@ public boolean getHasSmell() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { SleepyTest.ClassVisitor classVisitor; classVisitor = new SleepyTest.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); - } - - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -68,16 +55,16 @@ private class ClassVisitor extends VoidVisitorAdapter { // examine all methods in the test class @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { currentMethod = n; - testMethod = new TestMethod(n.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(false); //default value is false (i.e. no smell) super.visit(n, arg); testMethod.setHasSmell(sleepCount >= 1); testMethod.addDataItem("ThreadSleepCount", String.valueOf(sleepCount)); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; diff --git a/src/main/java/testsmell/smell/UnknownTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java similarity index 80% rename from src/main/java/testsmell/smell/UnknownTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java index 4c3d43c..8896936 100644 --- a/src/main/java/testsmell/smell/UnknownTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java @@ -1,4 +1,4 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.NodeList; @@ -7,10 +7,8 @@ import com.github.javaparser.ast.expr.MemberValuePair; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestMethod; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -19,26 +17,24 @@ public class UnknownTest extends AbstractSmell { - private List smellyElementList; + + private CompilationUnit testFileCompilationUnit; public UnknownTest() { - smellyElementList = new ArrayList<>(); + super(); } - /** - * Checks of 'Unknown Test' smell - */ @Override - public String getSmellName() { - return "Unknown Test"; + public AbstractSmell recreate() { + return new UnknownTest(); } /** - * Returns true if any of the elements has a smell + * Checks of 'Unknown Test' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "Unknown Test"; } /** @@ -48,15 +44,8 @@ public boolean getHasSmell() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { UnknownTest.ClassVisitor classVisitor; classVisitor = new UnknownTest.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); - } - - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } @@ -71,7 +60,7 @@ private class ClassVisitor extends VoidVisitorAdapter { // examine all methods in the test class @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { Optional assertAnnotation = n.getAnnotationByName("Test"); if (assertAnnotation.isPresent()) { for (int i = 0; i < assertAnnotation.get().getNodeLists().size(); i++) { @@ -85,7 +74,7 @@ public void visit(MethodDeclaration n, Void arg) { } } currentMethod = n; - testMethod = new TestMethod(n.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(false); //default value is false (i.e. no smell) super.visit(n, arg); @@ -93,7 +82,7 @@ public void visit(MethodDeclaration n, Void arg) { if (!hasAssert && !hasExceptionAnnotation) testMethod.setHasSmell(true); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; diff --git a/src/main/java/testsmell/smell/VerboseTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java similarity index 71% rename from src/main/java/testsmell/smell/VerboseTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java index 28346a9..08cca79 100644 --- a/src/main/java/testsmell/smell/VerboseTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java @@ -1,42 +1,36 @@ -package testsmell.smell; +package edu.rit.se.testsmells.testsmell.smell; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestMethod; -import testsmell.Util; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.List; /* If a test methods contains a statements that exceeds a certain threshold, the method is marked as smelly */ public class VerboseTest extends AbstractSmell { - private List smellyElementList; + + private CompilationUnit testFileCompilationUnit; public VerboseTest() { - smellyElementList = new ArrayList<>(); + super(); } - /** - * Checks of 'Verbose Test' smell - */ @Override - public String getSmellName() { - return "Verbose Test"; + public AbstractSmell recreate() { + return new VerboseTest(); } /** - * Returns true if any of the elements has a smell + * Checks of 'Verbose Test' smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public String getSmellName() { + return "Verbose Test"; } /** @@ -46,15 +40,8 @@ public boolean getHasSmell() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { VerboseTest.ClassVisitor classVisitor; classVisitor = new VerboseTest.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); - } - - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -66,9 +53,9 @@ private class ClassVisitor extends VoidVisitorAdapter { // examine all methods in the test class @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { currentMethod = n; - testMethod = new TestMethod(n.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(false); //default value is false (i.e. no smell) //method should not be abstract @@ -83,7 +70,7 @@ public void visit(MethodDeclaration n, Void arg) { testMethod.setHasSmell(verboseCount >= 1); testMethod.addDataItem("VerboseCount", String.valueOf(verboseCount)); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; diff --git a/src/main/java/testsmell/AbstractSmell.java b/src/main/java/testsmell/AbstractSmell.java deleted file mode 100644 index 26d6f9e..0000000 --- a/src/main/java/testsmell/AbstractSmell.java +++ /dev/null @@ -1,16 +0,0 @@ -package testsmell; - -import com.github.javaparser.ast.CompilationUnit; - -import java.io.FileNotFoundException; -import java.util.List; - -public abstract class AbstractSmell { - public abstract String getSmellName(); - - public abstract boolean getHasSmell(); - - public abstract void runAnalysis(CompilationUnit testFileCompilationUnit,CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException; - - public abstract List getSmellyElements(); -} diff --git a/src/main/java/testsmell/ResultsWriter.java b/src/main/java/testsmell/ResultsWriter.java deleted file mode 100644 index e980397..0000000 --- a/src/main/java/testsmell/ResultsWriter.java +++ /dev/null @@ -1,74 +0,0 @@ -package testsmell; - -import java.io.FileWriter; -import java.io.IOException; -import java.text.MessageFormat; -import java.util.Calendar; -import java.util.List; - -/** - * This class is utilized to write output to a CSV file - */ -public class ResultsWriter { - - private String outputFile; - private FileWriter writer; - - /** - * Creates the file into which output it to be written into. Results from each file will be stored in a new file - * @throws IOException - */ - private ResultsWriter() throws IOException { - String time = String.valueOf(Calendar.getInstance().getTimeInMillis()); - outputFile = MessageFormat.format("{0}_{1}_{2}.{3}", "Output","TestSmellDetection",time, "csv"); - writer = new FileWriter(outputFile,false); - } - - /** - * Factory method that provides a new instance of the ResultsWriter - * @return new ResultsWriter instance - * @throws IOException - */ - public static ResultsWriter createResultsWriter() throws IOException { - return new ResultsWriter(); - } - - /** - * Writes column names into the CSV file - * @param columnNames the column names - * @throws IOException - */ - public void writeColumnName(List columnNames) throws IOException { - writeOutput(columnNames); - } - - /** - * Writes column values into the CSV file - * @param columnValues the column values - * @throws IOException - */ - public void writeLine(List columnValues) throws IOException { - writeOutput(columnValues); - } - - /** - * Appends the input values into the CSV file - * @param dataValues the data that needs to be written into the file - * @throws IOException - */ - private void writeOutput(List dataValues)throws IOException { - writer = new FileWriter(outputFile,true); - - for (int i=0; i getData(); -} diff --git a/src/main/java/testsmell/TestFile.java b/src/main/java/testsmell/TestFile.java deleted file mode 100644 index 333ba84..0000000 --- a/src/main/java/testsmell/TestFile.java +++ /dev/null @@ -1,107 +0,0 @@ -package testsmell; - -import org.apache.commons.lang3.StringUtils; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class TestFile { - private String app, testFilePath, productionFilePath; - private List testSmells; - - public String getApp() { - return app; - } - - public String getProductionFilePath() { - return productionFilePath; - } - - public String getTestFilePath() { - return testFilePath; - } - - public List getTestSmells() { - return testSmells; - } - - public boolean getHasProductionFile() { - return ((productionFilePath != null && !productionFilePath.isEmpty())); - } - - public TestFile(String app, String testFilePath, String productionFilePath) { - this.app = app; - this.testFilePath = testFilePath; - this.productionFilePath = productionFilePath; - this.testSmells = new ArrayList<>(); - } - - public void addSmell(AbstractSmell smell) { - testSmells.add(smell); - } - - /** - * Supposed to return the version of the project. - * Returns the "N.I.Y", Not Implemented Yet string - * todo: not implemented in any way yet - */ - public String getTagName(){ - return "N.I.Y"; - } - - public String getTestFileName(){ - int lastIndex = testFilePath.lastIndexOf(File.separator); - return testFilePath.substring(lastIndex+1); - } - - public String getTestFileNameWithoutExtension(){ - int lastIndex = getTestFileName().lastIndexOf("."); - return getTestFileName().substring(0,lastIndex); - } - - public String getProductionFileNameWithoutExtension(){ - int lastIndex = getProductionFileName().lastIndexOf("."); - if(lastIndex==-1) - return ""; - return getProductionFileName().substring(0,lastIndex); - } - - public String getProductionFileName(){ - int lastIndex = productionFilePath.lastIndexOf(File.separator); - if(lastIndex==-1) - return ""; - return productionFilePath.substring(lastIndex+1); - } - - /** - * Returns the path of the test file relative to the folder with the name of the project. - * If the project directory has a different name, returns an empty string. - * @return the relative test file path - */ - public String getRelativeTestFilePath() { - if (!StringUtils.isEmpty(testFilePath)) { - int projectNameIndex = testFilePath.lastIndexOf(app); - if (projectNameIndex == -1) - return ""; - return testFilePath.substring(projectNameIndex+app.length()+File.separator.length()); - } else - return ""; - } - - /** - * Returns the path of the production file relative to the folder with the name of the project. - * If the project directory has a different name, returns an empty string. - * @return the relative production file path - * - */ - public String getRelativeProductionFilePath() { - if (!StringUtils.isEmpty(productionFilePath)) { - int projectNameIndex = productionFilePath.lastIndexOf(app); - if (projectNameIndex == -1) - return ""; - return productionFilePath.substring(projectNameIndex+app.length()+File.separator.length()); - } else - return ""; - } -} \ No newline at end of file diff --git a/src/main/java/testsmell/TestMethod.java b/src/main/java/testsmell/TestMethod.java deleted file mode 100644 index 6074b55..0000000 --- a/src/main/java/testsmell/TestMethod.java +++ /dev/null @@ -1,39 +0,0 @@ -package testsmell; - -import java.util.HashMap; -import java.util.Map; - -public class TestMethod extends SmellyElement { - - private String methodName; - private boolean hasSmell; - private Map data; - - public TestMethod(String methodName) { - this.methodName = methodName; - data = new HashMap<>(); - } - - public void setHasSmell(boolean hasSmell) { - this.hasSmell = hasSmell; - } - - public void addDataItem(String name, String value) { - data.put(name, value); - } - - @Override - public String getElementName() { - return methodName; - } - - @Override - public boolean getHasSmell() { - return hasSmell; - } - - @Override - public Map getData() { - return data; - } -} diff --git a/src/main/java/testsmell/TestSmellDetector.java b/src/main/java/testsmell/TestSmellDetector.java deleted file mode 100644 index a76fad3..0000000 --- a/src/main/java/testsmell/TestSmellDetector.java +++ /dev/null @@ -1,108 +0,0 @@ -package testsmell; - -import com.github.javaparser.JavaParser; -import com.github.javaparser.ast.CompilationUnit; -import org.apache.commons.lang3.StringUtils; -import testsmell.smell.*; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -public class TestSmellDetector { - - private List testSmells; - - /** - * Instantiates the various test smell analyzer classes and loads the objects into an List - */ - public TestSmellDetector() { - initializeSmells(); - } - - public TestSmellDetector(boolean initialize) {} - - private void initializeSmells(){ - testSmells = new ArrayList<>(); - testSmells.add(new AssertionRoulette()); - testSmells.add(new ConditionalTestLogic()); - testSmells.add(new ConstructorInitialization()); - testSmells.add(new DefaultTest()); - testSmells.add(new EmptyTest()); - testSmells.add(new ExceptionCatchingThrowing()); - testSmells.add(new GeneralFixture()); - testSmells.add(new MysteryGuest()); - testSmells.add(new PrintStatement()); - testSmells.add(new RedundantAssertion()); - testSmells.add(new SensitiveEquality()); - testSmells.add(new VerboseTest()); - testSmells.add(new SleepyTest()); - testSmells.add(new EagerTest()); - testSmells.add(new LazyTest()); - testSmells.add(new DuplicateAssert()); - testSmells.add(new UnknownTest()); - testSmells.add(new IgnoredTest()); - testSmells.add(new ResourceOptimism()); - testSmells.add(new MagicNumberTest()); - testSmells.add(new DependentTest()); - } - - public void setTestSmells(List testSmells) { - this.testSmells = testSmells; - } - - /** - * Factory method that provides a new instance of the TestSmellDetector - * - * @return new TestSmellDetector instance - */ - public static TestSmellDetector createTestSmellDetector() { - return new TestSmellDetector(); - } - - /** - * Provides the names of the smells that are being checked for in the code - * - * @return list of smell names - */ - public List getTestSmellNames() { - return testSmells.stream().map(AbstractSmell::getSmellName).collect(Collectors.toList()); - } - - /** - * Loads the java source code file into an AST and then analyzes it for the existence of the different types of test smells - */ - public TestFile detectSmells(TestFile testFile) throws IOException { - CompilationUnit testFileCompilationUnit=null, productionFileCompilationUnit=null; - FileInputStream testFileInputStream, productionFileInputStream; - - if(!StringUtils.isEmpty(testFile.getTestFilePath())) { - testFileInputStream = new FileInputStream(testFile.getTestFilePath()); - testFileCompilationUnit = JavaParser.parse(testFileInputStream); - } - - if(!StringUtils.isEmpty(testFile.getProductionFilePath())){ - productionFileInputStream = new FileInputStream(testFile.getProductionFilePath()); - productionFileCompilationUnit = JavaParser.parse(productionFileInputStream); - } - -// initializeSmells(); - for (AbstractSmell smell : testSmells) { - try { - smell.runAnalysis(testFileCompilationUnit, productionFileCompilationUnit,testFile.getTestFileNameWithoutExtension(),testFile.getProductionFileNameWithoutExtension()); - } catch (FileNotFoundException e) { - testFile.addSmell(null); - continue; - } - testFile.addSmell(smell); - } - - return testFile; - - } - - -} diff --git a/src/main/java/testsmell/Util.java b/src/main/java/testsmell/Util.java deleted file mode 100644 index b1830c7..0000000 --- a/src/main/java/testsmell/Util.java +++ /dev/null @@ -1,57 +0,0 @@ -package testsmell; - -import com.github.javaparser.ast.Modifier; -import com.github.javaparser.ast.body.MethodDeclaration; - -public class Util { - - public static boolean isValidTestMethod(MethodDeclaration n) { - boolean valid = false; - - if (!n.getAnnotationByName("Ignore").isPresent()) { - //only analyze methods that either have a @test annotation (Junit 4) or the method name starts with 'test' - if (n.getAnnotationByName("Test").isPresent() || n.getNameAsString().toLowerCase().startsWith("test")) { - //must be a public method - if (n.getModifiers().contains(Modifier.PUBLIC)) { - valid = true; - } - } - } - - return valid; - } - - public static boolean isValidSetupMethod(MethodDeclaration n) { - boolean valid = false; - - if (!n.getAnnotationByName("Ignore").isPresent()) { - //only analyze methods that either have a @Before annotation (Junit 4) or the method name is 'setUp' - if (n.getAnnotationByName("Before").isPresent() || n.getNameAsString().equals("setUp")) { - //must be a public method - if (n.getModifiers().contains(Modifier.PUBLIC)) { - valid = true; - } - } - } - - return valid; - } - - public static boolean isInt(String s) - { - try - { int i = Integer.parseInt(s); return true; } - - catch(NumberFormatException er) - { return false; } - } - - public static boolean isNumber(String str) { - try { - double v = Double.parseDouble(str); - return true; - } catch (NumberFormatException nfe) { - } - return false; - } -} diff --git a/src/main/java/testsmell/smell/DefaultTest.java b/src/main/java/testsmell/smell/DefaultTest.java deleted file mode 100644 index e630946..0000000 --- a/src/main/java/testsmell/smell/DefaultTest.java +++ /dev/null @@ -1,70 +0,0 @@ -package testsmell.smell; - -import com.github.javaparser.ast.CompilationUnit; -import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; -import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestClass; - -import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.List; - -/* -By default Android Studio creates default test classes when a project is created. These classes are meant to serve as an example for developers when wring unit tests -This code marks the class as smelly if the class name corresponds to the name of the default test classes - */ -public class DefaultTest extends AbstractSmell { - - private List smellyElementList; - - public DefaultTest() { - smellyElementList = new ArrayList<>(); - } - - /** - * Checks of 'Default Test' smell - */ - @Override - public String getSmellName() { - return "Default Test"; - } - - /** - * Returns true if any of the elements has a smell - */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; - } - - @Override - public void runAnalysis(CompilationUnit testFileCompilationUnit,CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { - DefaultTest.ClassVisitor classVisitor; - classVisitor = new DefaultTest.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); - } - - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; - } - - private class ClassVisitor extends VoidVisitorAdapter { - TestClass testClass; - - @Override - public void visit(ClassOrInterfaceDeclaration n, Void arg) { - if (n.getNameAsString().equals("ExampleUnitTest") || n.getNameAsString().equals("ExampleInstrumentedTest")) { - testClass = new TestClass(n.getNameAsString()); - testClass.setHasSmell(true); - smellyElementList.add(testClass); - } - super.visit(n, arg); - } - } -} diff --git a/src/resources/AssertionRoulette/agit-integration-tests/src/main/java/com/madgag/agit/GitAsyncTaskTest.java b/src/resources/AssertionRoulette/agit-integration-tests/src/main/java/com/madgag/agit/GitAsyncTaskTest.java new file mode 100644 index 0000000..fc8ed66 --- /dev/null +++ b/src/resources/AssertionRoulette/agit-integration-tests/src/main/java/com/madgag/agit/GitAsyncTaskTest.java @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2011, 2012 Roberto Tyley + * + * This file is part of 'Agit' - an Android Git client. + * + * Agit is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Agit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/ . + */ + +package com.madgag.agit; + +import static com.google.inject.util.Modules.override; +import static com.madgag.agit.GitTestUtils.DSA_USER; +import static com.madgag.agit.GitTestUtils.RSA_USER; +import static com.madgag.agit.GitTestUtils.integrationGitServerURIFor; +import static com.madgag.agit.git.Repos.remoteConfigFor; +import static com.madgag.agit.matchers.HasGitObjectMatcher.hasGitObject; +import static com.madgag.hamcrest.FileExistenceMatcher.exists; +import static com.madgag.hamcrest.FileLengthMatcher.ofLength; +import static java.lang.System.currentTimeMillis; +import static java.lang.Thread.currentThread; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static roboguice.RoboGuice.newDefaultRoboModule; +import android.app.Application; +import android.os.Looper; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.MediumTest; +import android.util.Log; + +import com.google.inject.Injector; +import com.madgag.agit.matchers.GitTestHelper; +import com.madgag.agit.operation.lifecycle.OperationLifecycleSupport; +import com.madgag.agit.operations.Clone; +import com.madgag.agit.operations.Fetch; +import com.madgag.agit.operations.GitAsyncTask; +import com.madgag.agit.operations.GitAsyncTaskFactory; +import com.madgag.agit.operations.GitOperation; +import com.madgag.agit.operations.OpNotification; +import com.madgag.agit.operations.Progress; +import com.madgag.agit.operations.Pull; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.CountDownLatch; + +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import org.eclipse.jgit.transport.RefSpec; +import org.eclipse.jgit.transport.RemoteConfig; +import org.eclipse.jgit.transport.URIish; + +import roboguice.RoboGuice; + +public class GitAsyncTaskTest extends ActivityInstrumentationTestCase2 { + + private static final String TAG = "GitAsyncTaskTest"; + + private Injector injector; + + public GitAsyncTaskTest() { + super("com.madgag.agit", DashboardActivity.class); + } + + private GitTestHelper helper() { + return AndroidTestEnvironment.helper(getInstrumentation()); + } + + @Override + public void setUp() throws ClassNotFoundException, InstantiationException, IllegalAccessException { + Application application = (Application) getInstrumentation().getTargetContext().getApplicationContext(); + injector = RoboGuice.setBaseApplicationInjector(application, RoboGuice.DEFAULT_STAGE, + newDefaultRoboModule(application), + override(new AgitModule()).with(new AgitIntegrationTestModule())); + } + + @Override + public void tearDown() { + RoboGuice.util.reset(); + } + + @MediumTest + public void testCloneRepoWithEmptyBlobInPack() throws Exception { + Clone cloneOp = new Clone(true, integrationGitServerURIFor("tiny-repo.with-empty-file.git"), + helper().newFolder()); + + Repository repo = executeAndWaitFor(cloneOp); + assertThat(repo, hasGitObject("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391")); // empty blob + assertThat(repo, hasGitObject("adcb77d8f74590f54b4c1919b322aed456b22aeb")); // populated blob + } + + @MediumTest + public void testCloneNonBareRepoFromLocalTestServer() throws Exception { + Clone cloneOp = new Clone(false, integrationGitServerURIFor("small-repo.early.git"), helper().newFolder()); + + Repository repo = executeAndWaitFor(cloneOp); + + assertThat(repo, hasGitObject("ba1f63e4430bff267d112b1e8afc1d6294db0ccc")); + + File readmeFile = new File(repo.getWorkTree(), "README"); + assertThat(readmeFile, exists()); + assertThat(readmeFile, ofLength(12)); + } + + @MediumTest + public void testFetchUpdatesOnCloneFromLocalTestServer() throws Exception { + Clone cloneOp = new Clone(true, integrationGitServerURIFor("small-test-repo.early.git"), helper().newFolder()); + + Repository repository = executeAndWaitFor(cloneOp); + + printFetchRefSpecs(repository); + + String initialCommitId = "3974996807a9f596cf25ac3a714995c24bb97e2c", commit1 = "ce1e0703402e989bedf03d5df535401340f54b42"; + assertThat(repository, hasGitObject(initialCommitId)); + assertThat(repository.resolve("master").name(), equalTo(initialCommitId)); + assertThat(repository, not(hasGitObject(commit1))); + + setRemoteUrl(repository, integrationGitServerURIFor("small-test-repo.later.git")); + + executeAndWaitFor(new Fetch(repository, DEFAULT_REMOTE_NAME)); + + assertThat(repository, hasGitObject(commit1)); + assertThat(repository.resolve("master").name(), equalTo(commit1)); + } + + private void printFetchRefSpecs(Repository repository) { + RemoteConfig remoteConfig = remoteConfigFor(repository, DEFAULT_REMOTE_NAME); + for (RefSpec refSpec : remoteConfig.getFetchRefSpecs()) { + Log.i(TAG, "refSpec = " + refSpec); + } + } + + @MediumTest + public void testFetchUpdatesFromLocalTestServer() throws Exception { + Repository repository = helper().unpackRepo("small-test-repo.early.bare.git.zap"); + setRemoteUrl(repository, integrationGitServerURIFor("small-test-repo.later.git")); + + String initialCommitId = "3974996807a9f596cf25ac3a714995c24bb97e2c", commit1 = "ce1e0703402e989bedf03d5df535401340f54b42"; + assertThat(repository, hasGitObject(initialCommitId)); + assertThat(repository.resolve("master").name(), equalTo(initialCommitId)); + assertThat(repository, not(hasGitObject(commit1))); + + executeAndWaitFor(new Fetch(repository, DEFAULT_REMOTE_NAME)); + + assertThat(repository, hasGitObject(commit1)); + assertThat(repository.resolve("master").name(), equalTo(commit1)); + } + + @MediumTest + public void testPullUpdatesFromLocalTestServer() throws Exception { + Repository repository = helper().unpackRepo("small-test-repo.early.zap"); + setRemoteUrl(repository, integrationGitServerURIFor("small-test-repo.later.git")); + // Git.wrap(repository).branchCreate().setName("master").setStartPoint("origin/master"); + + assertThat(repository, hasGitObject("3974996807a9f596cf25ac3a714995c24bb97e2c")); + String commit1 = "ce1e0703402e989bedf03d5df535401340f54b42"; + assertThat(repository, not(hasGitObject(commit1))); + assertFileLength(2, repository.getWorkTree(), "EXAMPLE"); + + executeAndWaitFor(new Pull(repository)); + + assertThat(repository, hasGitObject(commit1)); + + assertFileLength(4, repository.getWorkTree(), "EXAMPLE"); + } + + private void assertFileLength(int length, File workTree, String exampleFile) { + File readmeFile = new File(workTree, exampleFile); + assertThat(readmeFile, exists()); + assertThat("len=" + readmeFile.length(), readmeFile, ofLength(length)); + } + + @MediumTest + public void testCloneRepoUsingRSA() throws Exception { + Clone cloneOp = new Clone(true, integrationGitServerURIFor("small-repo.early.git").setUser(RSA_USER), + helper().newFolder()); + + assertThat(executeAndWaitFor(cloneOp), hasGitObject("ba1f63e4430bff267d112b1e8afc1d6294db0ccc")); + } + + @MediumTest + public void testCloneRepoUsingDSA() throws Exception { + Clone cloneOp = new Clone(true, integrationGitServerURIFor("small-repo.early.git").setUser(DSA_USER), + helper().newFolder()); + + assertThat(executeAndWaitFor(cloneOp), hasGitObject("ba1f63e4430bff267d112b1e8afc1d6294db0ccc")); + } + + @MediumTest + public void testSimpleReadOnlyCloneFromGitHub() throws Exception { + Clone cloneOp = new Clone(false, new URIish("git://github.com/agittest/small-project.git"), + helper().newFolder()); + Repository repo = executeAndWaitFor(cloneOp); + + assertThat(repo, hasGitObject("9e0b5e42b3e1c59bc83b55142a8c50dfae36b144")); + assertThat(repo, not(hasGitObject("111111111111111111111111111111111111cafe"))); + + File readmeFile = new File(repo.getWorkTree(), "README"); + assertThat(readmeFile, exists()); + } + + @MediumTest + public void testNonBareCloneFromRepoWithFiveMBBlobForIssue47() throws Exception { + Clone cloneOp = new Clone(false, new URIish("git://github.com/rtyley/five-mb-file-test-repo.git"), + helper().newFolder()); + Repository repo = executeAndWaitFor(cloneOp); + + assertThat(repo, hasGitObject("3995316735a53542acdf0d92e0b725fe296c0b49")); + assertThat(repo, not(hasGitObject("111111111111111111111111111111111111cafe"))); + + File bigFile = new File(repo.getWorkTree(), "5mb.zeros"); + assertThat(bigFile, exists()); + } + +// @LargeTest +// public void testCanCloneAllSuggestedRepos() throws Exception { +// for (SuggestedRepo suggestedRepo : SUGGESTIONS) { +// Repository repo = executeAndWaitFor(new Clone(true, new URIish(suggestedRepo.getURI()), tempFolder())); +// Map allRefs = repo.getAllRefs(); +// assertThat("Refs for " + suggestedRepo + " @ " + repo, allRefs.size(), greaterThan(0)); +// } +// } + + private Repository executeAndWaitFor(final GitOperation operation) + throws InterruptedException, IOException { + final CountDownLatch latch = new CountDownLatch(1); + Log.d(TAG, "About to start " + operation); + new Thread() { + public void run() { + Looper.prepare(); + Log.d(TAG, "In run method for " + operation); + GitAsyncTask task = injector.getInstance(GitAsyncTaskFactory.class).createTaskFor(operation, + new OperationLifecycleSupport() { + public void startedWith(OpNotification ongoingNotification) { + Log.i(TAG, "Started " + operation + " with " + ongoingNotification); + } + + public void publish(Progress progress) { + } + + public void error(OpNotification notification) { + Log.i(TAG, "Errored " + operation + " with " + notification); + } + + public void success(OpNotification completionNotification) { + } + + public void completed(OpNotification completionNotification) { + Log.i(TAG, "Completed " + operation + " with " + completionNotification); + latch.countDown(); + } + }); + task.execute(); + Log.d(TAG, "Called execute() on task for " + operation); + Looper.loop(); + } + }.start(); + long startTime = currentTimeMillis(); + Log.i(TAG, "Waiting for " + operation + " to complete - currentThread=" + currentThread()); + // http://stackoverflow.com/questions/5497324/why-arent-java-util-concurrent-timeunit-types-greater-than + // -seconds-available-in + boolean timeout = !latch.await(7 * 60, SECONDS); + long duration = currentTimeMillis() - startTime; + Log.i(TAG, "Finished waiting - timeout=" + timeout + " duration=" + duration); + assertThat("Timeout for " + operation, timeout, is(false)); + return FileRepositoryBuilder.create(operation.getGitDir()); + } + + private void setRemoteUrl(Repository repository, URIish uri) throws IOException { + RemoteConfig remoteConfig = remoteConfigFor(repository, DEFAULT_REMOTE_NAME); + for (URIish urIish : remoteConfig.getURIs()) { + remoteConfig.removeURI(urIish); + } + remoteConfig.addURI(uri); + remoteConfig.update(repository.getConfig()); + repository.getConfig().save(); + } + +} diff --git a/src/resources/ConditionalTestLogic/src/test/java/com/dalthed/tucan/EventsScraperTest.java b/src/resources/ConditionalTestLogic/src/test/java/com/dalthed/tucan/EventsScraperTest.java new file mode 100644 index 0000000..224e406 --- /dev/null +++ b/src/resources/ConditionalTestLogic/src/test/java/com/dalthed/tucan/EventsScraperTest.java @@ -0,0 +1,52 @@ +package com.dalthed.tucan; + +import android.widget.SpinnerAdapter; + +import com.dalthed.tucan.Connection.AnswerObject; +import com.dalthed.tucan.Connection.CookieManager; +import com.dalthed.tucan.scraper.EventsScraper; +import com.dalthed.tucan.testmodels.EventsModel; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricGradleTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import java.util.Map; + +import static org.junit.Assert.*; + +@RunWith(RobolectricGradleTestRunner.class) +@Config(constants = BuildConfig.class) +public class EventsScraperTest extends TestBase { + + public EventsScraperTest() { + this.resourcesBaseName = "Events"; + this.testClazzModel = EventsModel.class; + } + + @Test + public void testSpinner() { + + for (Map.Entry entry : sourcesMap.entrySet()) { + + String id = entry.getKey(); + Object resultObject = resultsMap.get(id); + if (resultObject instanceof EventsModel) { + EventsModel result = (EventsModel) resultObject; + if (result.testSpinner.runTest) { + System.out.println("Testing " + id + " (testSpinner)"); + //System.out.println(result); + AnswerObject answer = new AnswerObject(entry.getValue(), "", new CookieManager(), ""); + EventsScraper scraper = new EventsScraper(RuntimeEnvironment.application, answer); + SpinnerAdapter spinnerAdapter = scraper.spinnerAdapter(); + assertEquals(spinnerAdapter.getCount(), result.testSpinner.data.size()); + for (int i = 0; i < spinnerAdapter.getCount(); i++) { + assertEquals(spinnerAdapter.getItem(i), result.testSpinner.data.get(i)); + } + } + } + } + } +} diff --git a/src/resources/ConstructorInitialization/src/test/java/org/briarproject/bramble/crypto/TagEncodingTest.java b/src/resources/ConstructorInitialization/src/test/java/org/briarproject/bramble/crypto/TagEncodingTest.java new file mode 100644 index 0000000..6abb807 --- /dev/null +++ b/src/resources/ConstructorInitialization/src/test/java/org/briarproject/bramble/crypto/TagEncodingTest.java @@ -0,0 +1,59 @@ +package org.briarproject.bramble.crypto; + +import org.briarproject.bramble.api.Bytes; +import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.test.BrambleTestCase; +import org.briarproject.bramble.test.TestSecureRandomProvider; +import org.briarproject.bramble.test.TestUtils; +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; + +import static junit.framework.TestCase.assertTrue; +import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION; +import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; + +public class TagEncodingTest extends BrambleTestCase { + + private final CryptoComponent crypto; + private final SecretKey tagKey; + private final long streamNumber = 1234567890; + + public TagEncodingTest() { + crypto = new CryptoComponentImpl(new TestSecureRandomProvider()); + tagKey = TestUtils.getSecretKey(); + } + + @Test + public void testKeyAffectsTag() throws Exception { + Set set = new HashSet<>(); + for (int i = 0; i < 100; i++) { + byte[] tag = new byte[TAG_LENGTH]; + SecretKey tagKey = TestUtils.getSecretKey(); + crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber); + assertTrue(set.add(new Bytes(tag))); + } + } + + @Test + public void testProtocolVersionAffectsTag() throws Exception { + Set set = new HashSet<>(); + for (int i = 0; i < 100; i++) { + byte[] tag = new byte[TAG_LENGTH]; + crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION + i, streamNumber); + assertTrue(set.add(new Bytes(tag))); + } + } + + @Test + public void testStreamNumberAffectsTag() throws Exception { + Set set = new HashSet<>(); + for (int i = 0; i < 100; i++) { + byte[] tag = new byte[TAG_LENGTH]; + crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber + i); + assertTrue(set.add(new Bytes(tag))); + } + } +} diff --git a/src/resources/DefaultTest/src/test/java/com/app/missednotificationsreminder/ExampleUnitTest.java b/src/resources/DefaultTest/src/test/java/com/app/missednotificationsreminder/ExampleUnitTest.java new file mode 100644 index 0000000..18b65e4 --- /dev/null +++ b/src/resources/DefaultTest/src/test/java/com/app/missednotificationsreminder/ExampleUnitTest.java @@ -0,0 +1,676 @@ +package com.app.missednotificationsreminder; + +import com.app.missednotificationsreminder.binding.util.RxBindingUtils; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import rx.Observable; +import rx.Subscriber; +import rx.functions.Action1; +import rx.schedulers.Schedulers; +import rx.subscriptions.Subscriptions; +import timber.log.Timber; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * To work on unit tests, switch the Test Artifact in the Build Variants view. + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } + + @Test public void shareProblem() throws InterruptedException { + BindableObject begin = new BindableObject<>(0); + BindableObject rangeBegin = new BindableObject<>(0); + BindableObject mSchedulerRangeBegin = new BindableObject<>(0); + BindableObject beginTime = new BindableObject<>(null); + + begin.set(50); + rangeBegin.set(10); + mSchedulerRangeBegin.set(50); + + Observable beginChanged = valueChanged(begin) + .doOnEach(value -> System.out.println("Begin value changed: " + value.getValue())) + .doOnUnsubscribe(() -> System.out.println("Unshared?!")) + .share(); + + beginChanged + .skip(1)// skip initial value emitted automatically right after the + // subsription + .debounce(500, TimeUnit.MILLISECONDS)// such as range bar may change the + // value very quickly use the + // debounce function for the timeout + // based processing + .doOnEach(value -> System.out.println("Begin value changed 2: " + value.getValue())) + .subscribe(mSchedulerRangeBegin.asAction(), t -> t.printStackTrace()); + beginChanged + .map(minutes -> Integer.toString(minutes)) + .subscribe(beginTime.asAction(), t -> t.printStackTrace()); + beginChanged + .map(minutes -> minutes / 5) + .subscribe(rangeBegin.asAction(), t -> t.printStackTrace()); + + + valueChanged(rangeBegin) + .map(v -> v * 5) + .doOnEach(value -> Timber.d("Range begin value changed: " + value.getValue())) + .filter(minutes -> Math.abs(minutes - begin.get()) > 5) + .doOnEach(value -> Timber.d("Range begin filtered value changed: " + value.getValue())) + .subscribe(begin.asAction()); + + begin.set(100); + Thread.sleep(1000); + + assertEquals(beginTime.get(), "100"); + assertTrue(rangeBegin.get() == 20); + + Observable.just(200) + .subscribeOn(Schedulers.newThread()) + .subscribe(begin.asAction()); + begin.set(200); + Thread.sleep(1000); + + assertEquals(beginTime.get(), "200"); + assertTrue(rangeBegin.get() == 40); + + assertTrue(mSchedulerRangeBegin.get() == 200); + + begin.set(300); + Thread.sleep(1000); + + assertEquals(beginTime.get(), "300"); + assertTrue(rangeBegin.get() == 60); + + assertTrue(mSchedulerRangeBegin.get() == 300); + + } + + public static Observable valueChanged(BindableObject object) { + return Observable.create(new BindableObjectValueChangedOnSubscribe(object)); + } + + public static class BindableObjectValueChangedOnSubscribe implements Observable.OnSubscribe { + private final BindableObject mObject; + + /** + * Creates an BindableObjectValueChangedOnSubscribe for the given bindableObject + * + * @param bindableObject the bindable object to create + */ + public BindableObjectValueChangedOnSubscribe(BindableObject bindableObject) { + this.mObject = bindableObject; + } + + @Override public void call(final Subscriber subscriber) { + // create the property changed callback for the BindableObject which emits property value + // to the subscriber when it is changed + Observable2.OnPropertyChangedCallback callback = new Observable2.OnPropertyChangedCallback() { + @Override public void onPropertyChanged(Observable2 sender, int propertyId) { + if (!subscriber.isUnsubscribed()) { + subscriber.onNext(mObject.get()); + } + } + }; + mObject.addOnPropertyChangedCallback(callback); + + // when the subscription become unsubscribed remove added above property changed callback + subscriber.add( + Subscriptions.create( + () -> mObject.removeOnPropertyChangedCallback(callback))); + + // Emit initial value. + subscriber.onNext(mObject.get()); + } + } + + public class BindableObject extends BaseObservable { + T mValue; + + /** + * Creates an empty observable object + */ + public BindableObject() { + } + + /** + * Wraps the given object and creates an observable object + * + * @param value The value to be wrapped as an observable. + */ + public BindableObject(T value) { + mValue = value; + } + + /** + * Set the stored value. + */ + public void set(T value) { + if (!equals(mValue, value)) { + mValue = value; + notifyChange(); + } + } + + public T get() { + return mValue; + } + + /** + * Compare two objects of the same type. Supports null comparison as well as {@linkplain + * Object#equals(Object) Object.equals()} method. + *

+ * Override this method if you need additional comparison logic, for example custom string + * comparison. + * + * @param o1 + * @param o2 + * @return true if objects are equal, false otherwise + */ + public boolean equals(T o1, T o2) { + return (o1 == o2) || (o1 != null && o1.equals(o2)); + } + + /** + * An action which stores a new value for this object. + */ + public Action1 asAction() { + return new Action1() { + @Override public void call(T value) { + set(value); + } + }; + } + } + + public interface Observable2 { + + /** + * Adds a callback to listen for changes to the Observable. + * + * @param callback The callback to start listening. + */ + void addOnPropertyChangedCallback(OnPropertyChangedCallback callback); + + /** + * Removes a callback from those listening for changes. + * + * @param callback The callback that should stop listening. + */ + void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback); + + /** + * The callback that is called by Observable when an observable property has changed. + */ + abstract class OnPropertyChangedCallback { + + /** + * Called by an Observable whenever an observable property changes. + * + * @param sender The Observable that is changing. + * @param propertyId The BR identifier of the property that has changed. The getter + * for this property should be annotated with {Bindable}. + */ + public abstract void onPropertyChanged(Observable2 sender, int propertyId); + } + } + + public class BaseObservable implements Observable2 { + private transient PropertyChangeRegistry mCallbacks; + + public BaseObservable() { + } + + @Override + public synchronized void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) { + if (mCallbacks == null) { + mCallbacks = new PropertyChangeRegistry(); + } + mCallbacks.add(callback); + } + + @Override + public synchronized void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) { + if (mCallbacks != null) { + mCallbacks.remove(callback); + } + } + + /** + * Notifies listeners that all properties of this instance have changed. + */ + public synchronized void notifyChange() { + if (mCallbacks != null) { + mCallbacks.notifyCallbacks(this, 0, null); + } + } + + /** + * Notifies listeners that a specific property has changed. The getter for the property + * that changes should be marked with Bindable to generate a field in + * BR to be used as fieldId. + * + * @param fieldId The generated BR id for the Bindable field. + */ + public void notifyPropertyChanged(int fieldId) { + if (mCallbacks != null) { + mCallbacks.notifyCallbacks(this, fieldId, null); + } + } + } + + public static class PropertyChangeRegistry extends + CallbackRegistry { + + private static final CallbackRegistry.NotifierCallback NOTIFIER_CALLBACK = new CallbackRegistry.NotifierCallback() { + @Override + public void onNotifyCallback(Observable2.OnPropertyChangedCallback callback, Observable2 sender, + int arg, Void notUsed) { + callback.onPropertyChanged(sender, arg); + } + }; + + public PropertyChangeRegistry() { + super(NOTIFIER_CALLBACK); + } + + /** + * Notifies registered callbacks that a specific property has changed. + * + * @param observable The Observable that has changed. + * @param propertyId The BR id of the property that has changed or BR._all if the entire + * Observable has changed. + */ + public void notifyChange(Observable2 observable, int propertyId) { + notifyCallbacks(observable, propertyId, null); + } + } + + public static class CallbackRegistry implements Cloneable { + private static final String TAG = "CallbackRegistry"; + + /** + * An ordered collection of listeners waiting to be notified. + */ + private List mCallbacks = new ArrayList(); + + /** + * A bit flag for the first 64 listeners that are removed during notification. + * The lowest significant bit corresponds to the 0th index into mCallbacks. + * For a small number of callbacks, no additional array of objects needs to + * be allocated. + */ + private long mFirst64Removed = 0x0; + + /** + * Bit flags for the remaining callbacks that are removed during notification. + * When there are more than 64 callbacks and one is marked for removal, a dynamic + * array of bits are allocated for the callbacks. + */ + private long[] mRemainderRemoved; + + /** + * The recursion level of the notification + */ + private int mNotificationLevel; + + /** + * The notification mechanism for notifying an event. + */ + private final NotifierCallback mNotifier; + + /** + * Creates an EventRegistry that notifies the event with notifier. + * + * @param notifier The class to use to notify events. + */ + public CallbackRegistry(NotifierCallback notifier) { + mNotifier = notifier; + } + + /** + * Notify all callbacks. + * + * @param sender The originator. This is an opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)} + * @param arg An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)} + * @param arg2 An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)} + */ + public synchronized void notifyCallbacks(T sender, int arg, A arg2) { + mNotificationLevel++; + notifyRecurse(sender, arg, arg2); + mNotificationLevel--; + if (mNotificationLevel == 0) { + if (mRemainderRemoved != null) { + for (int i = mRemainderRemoved.length - 1; i >= 0; i--) { + final long removedBits = mRemainderRemoved[i]; + if (removedBits != 0) { + removeRemovedCallbacks((i + 1) * Long.SIZE, removedBits); + mRemainderRemoved[i] = 0; + } + } + } + if (mFirst64Removed != 0) { + removeRemovedCallbacks(0, mFirst64Removed); + mFirst64Removed = 0; + } + } + } + + /** + * Notify up to the first Long.SIZE callbacks that don't have a bit set in removed. + * + * @param sender The originator. This is an opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)} + * @param arg An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)} + * @param arg2 An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)} + */ + private void notifyFirst64(T sender, int arg, A arg2) { + final int maxNotified = Math.min(Long.SIZE, mCallbacks.size()); + notifyCallbacks(sender, arg, arg2, 0, maxNotified, mFirst64Removed); + } + + /** + * Notify all callbacks using a recursive algorithm to avoid allocating on the heap. + * This part captures the callbacks beyond Long.SIZE that have no bits allocated for + * removal before it recurses into {@link #notifyRemainder(Object, int, A, int)}. + *

+ *

Recursion is used to avoid allocating temporary state on the heap.

+ * + * @param sender The originator. This is an opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)} + * @param arg An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)} + * @param arg2 An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)} + */ + private void notifyRecurse(T sender, int arg, A arg2) { + final int callbackCount = mCallbacks.size(); + final int remainderIndex = mRemainderRemoved == null ? -1 : mRemainderRemoved.length - 1; + + // Now we've got all callbakcs that have no mRemainderRemoved value, so notify the + // others. + notifyRemainder(sender, arg, arg2, remainderIndex); + + // notifyRemainder notifies all at maxIndex, so we'd normally start at maxIndex + 1 + // However, we must also keep track of those in mFirst64Removed, so we add 2 instead: + final int startCallbackIndex = (remainderIndex + 2) * Long.SIZE; + + // The remaining have no bit set + notifyCallbacks(sender, arg, arg2, startCallbackIndex, callbackCount, 0); + } + + /** + * Notify callbacks that have mRemainderRemoved bits set for remainderIndex. If + * remainderIndex is -1, the first 64 will be notified instead. + * + * @param sender The originator. This is an opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)} + * @param arg An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)} + * @param arg2 An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)} + * @param remainderIndex The index into mRemainderRemoved that should be notified. + */ + private void notifyRemainder(T sender, int arg, A arg2, int remainderIndex) { + if (remainderIndex < 0) { + notifyFirst64(sender, arg, arg2); + } else { + final long bits = mRemainderRemoved[remainderIndex]; + final int startIndex = (remainderIndex + 1) * Long.SIZE; + final int endIndex = Math.min(mCallbacks.size(), startIndex + Long.SIZE); + notifyRemainder(sender, arg, arg2, remainderIndex - 1); + notifyCallbacks(sender, arg, arg2, startIndex, endIndex, bits); + } + } + + /** + * Notify callbacks from startIndex to endIndex, using bits as the bit status + * for whether they have been removed or not. bits should be from mRemainderRemoved or + * mFirst64Removed. bits set to 0 indicates that all callbacks from startIndex to + * endIndex should be notified. + * + * @param sender The originator. This is an opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)} + * @param arg An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)} + * @param arg2 An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)} + * @param startIndex The index into the mCallbacks to start notifying. + * @param endIndex One past the last index into mCallbacks to notify. + * @param bits A bit field indicating which callbacks have been removed and shouldn't + * be notified. + */ + private void notifyCallbacks(T sender, int arg, A arg2, final int startIndex, + final int endIndex, final long bits) { + long bitMask = 1; + for (int i = startIndex; i < endIndex; i++) { + if ((bits & bitMask) == 0) { + mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2); + } + bitMask <<= 1; + } + } + + /** + * Add a callback to be notified. If the callback is already in the list, another won't + * be added. This does not affect current notifications. + * + * @param callback The callback to add. + */ + public synchronized void add(C callback) { + int index = mCallbacks.lastIndexOf(callback); + if (index < 0 || isRemoved(index)) { + mCallbacks.add(callback); + } + } + + /** + * Returns true if the callback at index has been marked for removal. + * + * @param index The index into mCallbacks to check. + * @return true if the callback at index has been marked for removal. + */ + private boolean isRemoved(int index) { + if (index < Long.SIZE) { + // It is in the first 64 callbacks, just check the bit. + final long bitMask = 1L << index; + return (mFirst64Removed & bitMask) != 0; + } else if (mRemainderRemoved == null) { + // It is after the first 64 callbacks, but nothing else was marked for removal. + return false; + } else { + final int maskIndex = (index / Long.SIZE) - 1; + if (maskIndex >= mRemainderRemoved.length) { + // There are some items in mRemainderRemoved, but nothing at the given index. + return false; + } else { + // There is something marked for removal, so we have to check the bit. + final long bits = mRemainderRemoved[maskIndex]; + final long bitMask = 1L << (index % Long.SIZE); + return (bits & bitMask) != 0; + } + } + } + + /** + * Removes callbacks from startIndex to startIndex + Long.SIZE, based + * on the bits set in removed. + * + * @param startIndex The index into the mCallbacks to start removing callbacks. + * @param removed The bits indicating removal, where each bit is set for one callback + * to be removed. + */ + private void removeRemovedCallbacks(int startIndex, long removed) { + // The naive approach should be fine. There may be a better bit-twiddling approach. + final int endIndex = startIndex + Long.SIZE; + + long bitMask = 1L << (Long.SIZE - 1); + for (int i = endIndex - 1; i >= startIndex; i--) { + if ((removed & bitMask) != 0) { + mCallbacks.remove(i); + } + bitMask >>>= 1; + } + } + + /** + * Remove a callback. This callback won't be notified after this call completes. + * + * @param callback The callback to remove. + */ + public synchronized void remove(C callback) { + if (mNotificationLevel == 0) { + mCallbacks.remove(callback); + } else { + int index = mCallbacks.lastIndexOf(callback); + if (index >= 0) { + setRemovalBit(index); + } + } + } + + private void setRemovalBit(int index) { + if (index < Long.SIZE) { + // It is in the first 64 callbacks, just check the bit. + final long bitMask = 1L << index; + mFirst64Removed |= bitMask; + } else { + final int remainderIndex = (index / Long.SIZE) - 1; + if (mRemainderRemoved == null) { + mRemainderRemoved = new long[mCallbacks.size() / Long.SIZE]; + } else if (mRemainderRemoved.length < remainderIndex) { + // need to make it bigger + long[] newRemainders = new long[mCallbacks.size() / Long.SIZE]; + System.arraycopy(mRemainderRemoved, 0, newRemainders, 0, mRemainderRemoved.length); + mRemainderRemoved = newRemainders; + } + final long bitMask = 1L << (index % Long.SIZE); + mRemainderRemoved[remainderIndex] |= bitMask; + } + } + + /** + * Makes a copy of the registered callbacks and returns it. + * + * @return a copy of the registered callbacks. + */ + public synchronized ArrayList copyCallbacks() { + ArrayList callbacks = new ArrayList(mCallbacks.size()); + int numListeners = mCallbacks.size(); + for (int i = 0; i < numListeners; i++) { + if (!isRemoved(i)) { + callbacks.add(mCallbacks.get(i)); + } + } + return callbacks; + } + + /** + * Modifies callbacks to contain all callbacks in the CallbackRegistry. + * + * @param callbacks modified to contain all callbacks registered to receive events. + */ + public synchronized void copyCallbacks(List callbacks) { + callbacks.clear(); + int numListeners = mCallbacks.size(); + for (int i = 0; i < numListeners; i++) { + if (!isRemoved(i)) { + callbacks.add(mCallbacks.get(i)); + } + } + } + + /** + * Returns true if there are no registered callbacks or false otherwise. + * + * @return true if there are no registered callbacks or false otherwise. + */ + public synchronized boolean isEmpty() { + if (mCallbacks.isEmpty()) { + return true; + } else if (mNotificationLevel == 0) { + return false; + } else { + int numListeners = mCallbacks.size(); + for (int i = 0; i < numListeners; i++) { + if (!isRemoved(i)) { + return false; + } + } + return true; + } + } + + /** + * Removes all callbacks from the list. + */ + public synchronized void clear() { + if (mNotificationLevel == 0) { + mCallbacks.clear(); + } else if (!mCallbacks.isEmpty()) { + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + setRemovalBit(i); + } + } + } + + /** + * @return A copy of the CallbackRegistry with all callbacks listening to both instances. + */ + @SuppressWarnings("unchecked") + public synchronized CallbackRegistry clone() { + CallbackRegistry clone = null; + try { + clone = (CallbackRegistry) super.clone(); + clone.mFirst64Removed = 0; + clone.mRemainderRemoved = null; + clone.mNotificationLevel = 0; + clone.mCallbacks = new ArrayList(); + final int numListeners = mCallbacks.size(); + for (int i = 0; i < numListeners; i++) { + if (!isRemoved(i)) { + clone.mCallbacks.add(mCallbacks.get(i)); + } + } + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + return clone; + } + + /** + * Class used to notify events from CallbackRegistry. + * + * @param The callback type. + * @param The notification sender type. Typically this is the containing class. + * @param An opaque argument to pass to the notifier + */ + public abstract static class NotifierCallback { + /** + * Called by CallbackRegistry during + * {@link CallbackRegistry#notifyCallbacks(Object, int, Object)}} to notify the callback. + * + * @param callback The callback to notify. + * @param sender The opaque sender object. + * @param arg The opaque notification parameter. + * @param arg2 An opaque argument passed in + * {@link CallbackRegistry#notifyCallbacks} + * @see CallbackRegistry#CallbackRegistry(CallbackRegistry.NotifierCallback) + */ + public abstract void onNotifyCallback(C callback, T sender, int arg, A arg2); + } + } + +} \ No newline at end of file diff --git a/src/resources/DuplicateAssert/src/test/java/org/openbmap/utils/XmlSanitizerTest.java b/src/resources/DuplicateAssert/src/test/java/org/openbmap/utils/XmlSanitizerTest.java new file mode 100644 index 0000000..e542ee4 --- /dev/null +++ b/src/resources/DuplicateAssert/src/test/java/org/openbmap/utils/XmlSanitizerTest.java @@ -0,0 +1,41 @@ +package org.openbmap.utils; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class XmlSanitizerTest { + + @Test + public void testXmlSanitizer() { + boolean valid = XmlSanitizer.isValid("Fritzbox"); + assertEquals("Fritzbox is valid", true, valid); + System.out.println("Pure ASCII test - passed"); + + valid = XmlSanitizer.isValid("Fritz Box"); + assertEquals("Spaces are valid", true, valid); + System.out.println("Spaces test - passed"); + + valid = XmlSanitizer.isValid("Frützbüx"); + assertEquals("Frützbüx is invalid", false, valid); + System.out.println("No ASCII test - passed"); + + valid = XmlSanitizer.isValid("Fritz!box"); + assertEquals("Exclamation mark is valid", true, valid); + System.out.println("Exclamation mark test - passed"); + + valid = XmlSanitizer.isValid("Fritz.box"); + assertEquals("Exclamation mark is valid", true, valid); + System.out.println("Dot test - passed"); + + valid = XmlSanitizer.isValid("Fritz-box"); + assertEquals("Minus is valid", true, valid); + System.out.println("Minus test - passed"); + + valid = XmlSanitizer.isValid("Fritz-box"); + assertEquals("Minus is valid", true, valid); + System.out.println("Minus test - passed"); + } + +} + diff --git a/src/resources/EagerTest/src/main/java/com/mendhak/gpslogger/loggers/nmea/NmeaSentence.java b/src/resources/EagerTest/src/main/java/com/mendhak/gpslogger/loggers/nmea/NmeaSentence.java new file mode 100644 index 0000000..f2f237f --- /dev/null +++ b/src/resources/EagerTest/src/main/java/com/mendhak/gpslogger/loggers/nmea/NmeaSentence.java @@ -0,0 +1,89 @@ +package com.mendhak.gpslogger.loggers.nmea; + + +import com.mendhak.gpslogger.common.Strings; + +public class NmeaSentence { + + String[] nmeaParts; + + public NmeaSentence(String nmeaSentence){ + if(Strings.isNullOrEmpty(nmeaSentence)){ + nmeaParts = new String[]{""}; + return; + } + nmeaParts = nmeaSentence.split(","); + + } + + public boolean isLocationSentence(){ + return nmeaParts[0].equalsIgnoreCase("$GPGSA") || nmeaParts[0].equalsIgnoreCase("$GPGGA"); + } + + public String getLatestPdop(){ + if (nmeaParts[0].equalsIgnoreCase("$GPGSA")) { + + if (nmeaParts.length > 15 && !Strings.isNullOrEmpty(nmeaParts[15])) { + return nmeaParts[15]; + } + } + + return null; + } + + public String getLatestVdop(){ + if (nmeaParts[0].equalsIgnoreCase("$GPGSA")) { + if (nmeaParts.length > 17 &&!Strings.isNullOrEmpty(nmeaParts[17]) && !nmeaParts[17].startsWith("*")) { + return nmeaParts[17].split("\\*")[0]; + } + } + + return null; + } + + public String getLatestHdop(){ + if (nmeaParts[0].equalsIgnoreCase("$GPGGA")) { + if (nmeaParts.length > 8 &&!Strings.isNullOrEmpty(nmeaParts[8])) { + return nmeaParts[8]; + } + } + else if (nmeaParts[0].equalsIgnoreCase("$GPGSA")) { + if (nmeaParts.length > 16 &&!Strings.isNullOrEmpty(nmeaParts[16])) { + return nmeaParts[16]; + } + } + + return null; + } + + public String getGeoIdHeight(){ + if (nmeaParts[0].equalsIgnoreCase("$GPGGA")) { + if (nmeaParts.length > 11 &&!Strings.isNullOrEmpty(nmeaParts[11])) { + return nmeaParts[11]; + } + } + + return null; + } + + public String getAgeOfDgpsData(){ + if (nmeaParts[0].equalsIgnoreCase("$GPGGA")) { + if (nmeaParts.length > 13 && !Strings.isNullOrEmpty(nmeaParts[13])) { + return nmeaParts[13]; + } + } + + return null; + } + + public String getDgpsId(){ + if (nmeaParts[0].equalsIgnoreCase("$GPGGA")) { + if (nmeaParts.length > 14 &&!Strings.isNullOrEmpty(nmeaParts[14]) && !nmeaParts[14].startsWith("*")) { + return nmeaParts[14].split("\\*")[0]; + } + } + + return null; + } + +} diff --git a/src/resources/EagerTest/src/test/java/com/mendhak/gpslogger/loggers/nmea/NmeaSentenceTest.java b/src/resources/EagerTest/src/test/java/com/mendhak/gpslogger/loggers/nmea/NmeaSentenceTest.java new file mode 100644 index 0000000..4b24140 --- /dev/null +++ b/src/resources/EagerTest/src/test/java/com/mendhak/gpslogger/loggers/nmea/NmeaSentenceTest.java @@ -0,0 +1,85 @@ +package com.mendhak.gpslogger.loggers.nmea; + +import android.test.suitebuilder.annotation.SmallTest; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + + +@SmallTest +@RunWith(MockitoJUnitRunner.class) +public class NmeaSentenceTest { + + @Test + public void NmeaSentence_EmptyNmeaSentence_VDOPIsNull(){ + NmeaSentence nmeaSentence = new NmeaSentence("blahasdfasdf"); + assertThat("VDOP null by default", nmeaSentence.getLatestVdop(), nullValue()); + } + + @Test + public void NmeaSentence_EmptyNmeaSentence_HDOPIsNull(){ + + NmeaSentence nmeaSentence = new NmeaSentence("$GPGGA,,,,,,,,,,,,,,*47"); + assertThat("HDOP null by default", nmeaSentence.getLatestHdop(), nullValue()); + } + + @Test + public void NmeaSentence_EmptyNmeaSentence_DGPSIDIsNull(){ + + NmeaSentence nmeaSentence = new NmeaSentence("$GPGGA,,"); + assertThat("DGPSID null by default", nmeaSentence.getDgpsId(), nullValue()); + + nmeaSentence = new NmeaSentence(""); + assertThat("DGPSID null by default", nmeaSentence.getDgpsId(), nullValue()); + } + + @Test + public void NmeaSentence_GPGGA_ReadValidValues(){ + NmeaSentence nmeaSentence = new NmeaSentence("$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,27*47"); + assertThat("GPGGA - read HDOP", nmeaSentence.getLatestHdop(), is("0.9")); + assertThat("GPGGA - read GeoIdHeight", nmeaSentence.getGeoIdHeight(), is("46.9")); + assertThat("GPGGA - read Last dgps update", nmeaSentence.getAgeOfDgpsData(), nullValue()); + assertThat("GPGGA - read dgps station id", nmeaSentence.getDgpsId(), is("27")); + } + + @Test + public void NmeaSentence_GPGSA_ReadValidValues(){ + + NmeaSentence nmeaSentence = new NmeaSentence("$GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39"); + assertThat("GPGSA - read PDOP", nmeaSentence.getLatestPdop(), is("2.5")); + assertThat("GPGSA - read HDOP", nmeaSentence.getLatestHdop(), is("1.3")); + assertThat("GPGSA - read VDOP", nmeaSentence.getLatestVdop(), is("2.1")); + } + + @Test + public void NmeaSentence_Incomplete_ReadSomeValues(){ + + NmeaSentence nmeaSentence = new NmeaSentence("$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545"); + assertThat("GPGGA - Incomplete, read HDOP", nmeaSentence.getLatestHdop(), is("0.9")); + assertThat("GPGGA - Incomplete, no GeoIdHeight", nmeaSentence.getGeoIdHeight(), nullValue()); + } + + @Test + public void NmeaSentence_Null_NoValuesRead(){ + + NmeaSentence nmeaSentence = new NmeaSentence(null); + assertThat("Null NMEA string", nmeaSentence.getLatestHdop(), nullValue()); + } + + @Test + public void NmeaSentence_CheckForRelevantSentence(){ + + NmeaSentence nmeaSentence = new NmeaSentence(null); + assertThat("Null NMEA is not a valid location", nmeaSentence.isLocationSentence(), is(false)); + + nmeaSentence = new NmeaSentence("$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545"); + assertThat("GPGGA is a valid location", nmeaSentence.isLocationSentence(), is(true)); + + nmeaSentence = new NmeaSentence("$GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39"); + assertThat("GPGSA is a valid location", nmeaSentence.isLocationSentence(), is(true)); + } +} \ No newline at end of file diff --git a/src/resources/EmptyTest/src/androidTest/java/com/actisec/clipcaster/parser/LastPassParserTest.java b/src/resources/EmptyTest/src/androidTest/java/com/actisec/clipcaster/parser/LastPassParserTest.java new file mode 100644 index 0000000..f2a77fb --- /dev/null +++ b/src/resources/EmptyTest/src/androidTest/java/com/actisec/clipcaster/parser/LastPassParserTest.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2014 Xiao Bao Clark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those + * of the authors and should not be interpreted as representing official policies, + * either expressed or implied, of the FreeBSD Project. + */ + +package com.actisec.clipcaster.parser; + +import android.util.Log; + +import com.actisec.clipcaster.AbstractJavaScriptTestCase; +import com.actisec.clipcaster.ScrapedCredentials; +import com.actisec.clipcaster.ScrapedData; +import com.actisec.clipcaster.ScrapedDataHandler; +import com.actisec.clipcaster.Source; +import com.actisec.clipcaster.util.JavaScript; + +import org.apache.commons.lang3.StringEscapeUtils; +import org.codehaus.jackson.map.ObjectMapper; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class LastPassParserTest extends AbstractJavaScriptTestCase { + + private static final String FULL_SAMPLE_v1 = "script:(function(){var l_fs, l_bf=null, l_err=false;var l_bni=0, l_bnp=0;var l_bte=null, l_bpe=null, l_cpe=null;var l_w; var l_d; try { l_w=window.top; l_d=l_w.document;} catch (l_e){ l_w=window; l_d=document;}var l_iv=function(el, sf){ while (el&&(!sf||el.tagName != 'FORM')){ if (el.hasOwnProperty('style')&&(el.style['display']=='none'||el.style['visibility']=='hidden')) return false; else el=el.parentNode;} return true;};var l_cpp=/(?:existing|old|curr).*pass/i;for(var l_k=-1; l_k < l_w.frames.length; l_k++){ if(l_k==-1){ l_fs=l_d.getElementsByTagName('form');}else{ try{ l_w[l_k].document.domain } catch(e){console.log(e); l_err=true; continue;} l_fs=l_w[l_k].document.getElementsByTagName('form');} for (var l_i=0; l_i < l_fs.length; l_i++){ if (!l_iv(l_fs[l_i])) continue; var l_fe=l_fs[l_i].elements; var l_ni=0, l_np=0; var l_te=null, l_pe=null; for (var l_j=0; l_j < l_fe.length; l_j++){ var l_e=l_fe[l_j]; if ((l_e.type=='text'||l_e.type=='email'||l_e.type=='tel')&&l_iv(l_e, true)){ if (l_ni==0){ l_te=l_e;} l_ni++;} if (l_e.type=='password'&&l_iv(l_e, true)){ if (l_np==0){ l_pe=l_e;} l_np++; if (l_cpp.test(l_e.name)||l_cpp.test(l_e.id)){ l_cpe=l_e;} } } if (l_np==1){ if (!l_bf||(l_ni==1&&l_bni != 1)){ l_bni=l_ni; l_bnp=l_np; l_bf=l_fs[l_i]; l_bte=l_te; l_bpe=l_pe;} } else if (l_np > 1&&l_cpe){ l_bf=l_fs[l_i]; l_bpe=l_cpe;} }}var l_sfv=function(el, v){ try { var c=true; if (el.type=='select-one'&&el.value==v){ c=false;} el.value=v; if (c){ var evt=el.ownerDocument.createEvent('Events'); evt.initEvent('change', true, true); el.dispatchEvent(evt); evt=el.ownerDocument.createEvent('Events'); evt.initEvent('input', true, true); el.dispatchEvent(evt);} } catch(e){}};if (l_bf){ var do_fill=true; if (do_fill){ console.log('fill login form=' + (l_bf.id||l_bf.name)); if (l_bte){ l_sfv(l_bte, decodeURIComponent(escape(atob('dXNlckBleGFtcGxlLmNvbQ=='))));} l_sfv(l_bpe, decodeURIComponent(escape(atob('cDRzc3cwcmQ='))));}} else { console.log('no form');}})();////////////////////////////////////////////////////////////////////////////////////////////////////"; + + private LastPassParser mParser = new LastPassParser(); + + private String formatUserPassToJscript(String encodedUser, String encodedPass) { + return "(atob('" + encodedUser + "'))))." + "(atob('" + encodedPass + "'))))"; + } + + private class DummyCallback implements ScrapedDataHandler{ + + final List data = new ArrayList(); + @Override + public void handleData(ScrapedData scrapedData) { + synchronized (data) { + data.add(scrapedData); + data.notifyAll(); + } + } + } + + public void testCredTmp() throws Throwable{ + DummyCallback handler = new DummyCallback(); + Source source = mTestUtils.readSource(com.actisec.clipcaster.test.R.raw.lastpass_v3); + JavaScript javaScript = new JavaScript(source.javascriptProgram); + final long injectedTime = source.timeOfNotification; + + LastPassParser.Parser parser = new LastPassParser.Parser(getContext(),handler,injectedTime); + parser.getData(source.javascriptProgram); + + synchronized (handler.data) { + while (handler.data.isEmpty()) { + handler.data.wait(2000); + } + ScrapedData data = handler.data.remove(0); + assertNotNull(data); + final ScrapedCredentials creds = data.creds; + assertNotNull(creds); + + assertTrue(creds.user != null || creds.pass != null); + if(creds.user != null){ + assertEquals("user@example.com",creds.user); + } else if (creds.pass != null){ + assertEquals("p4ssw0rd", creds.pass); + } + } + } + + + +// private ScrapedCredentials innerCredTest(String content) throws Throwable{ +// final ScrapedData data = mParser.getScrapedData(mActivity, content); +// assertNotNull(data); +// final ScrapedCredentials creds = data.creds; +// assertNotNull(creds); +// assertNotNull(creds.user); +// assertNotNull(creds.pass); +// assertFalse(creds.user.isEmpty()); +// assertFalse(creds.pass.isEmpty()); +// return creds; +// } + + + public void testCredGetJustBase64() throws Throwable{ +// innerCredTest(formatUserPassToJscript("czRmM3A0c3N3MHJk", "dW9hZmlyZWRyb2lk")); + } + + public void testCredGetJustBase64WithEquals() throws Throwable{ +// innerCredTest(formatUserPassToJscript("bGVhc3VyZS4=", "c3VyZS4=")); + } + + public void testCredGetFullSampleV1() throws Throwable{ +// ScrapedCredentials credentials = innerCredTest(FULL_SAMPLE_v1); +// assertEquals("p4ssw0rd", credentials.pass); +// assertEquals("user@example.com",credentials.user); + + } + + public void testTestReadingTimeFromFile() throws Throwable{ + Source source = mTestUtils.readSource(com.actisec.clipcaster.test.R.raw.lastpass_v3); + assertNotNull(source); + assertEquals(1418693814750L,source.timeOfNotification); + } + + public void testGetCredsFromJs() throws Throwable { + Source source = mTestUtils.readSource(com.actisec.clipcaster.test.R.raw.lastpass_v3); +// ScrapedCredentials credentials = LastPassParser.getCredsFromJs(mActivity, source.javascriptProgram, source.timeOfNotification); +// assertNotNull(credentials); +// assertEquals("user@example.com", credentials.user); +// assertEquals("p4ssw0rd", credentials.pass); + + } + + public void testTestEscapedChars() throws Throwable { + String source = mTestUtils.readString(com.actisec.clipcaster.test.R.raw.escapedprotocol); + assertTrue(StringEscapeUtils.escapeJava(source), source.contains("/https?:\\/\\//")); + } + public void testSourceEscapedChars() throws Throwable { + String raw = mTestUtils.readString(com.actisec.clipcaster.test.R.raw.lastpass_v3); + Source source = new ObjectMapper().readValue(raw,Source.class); + + assertTrue(StringEscapeUtils.escapeJava(source.javascriptProgram),source.javascriptProgram.contains("/https?:\\/\\//")); + } + + public void testGetFunction() throws Throwable { + Source source = mTestUtils.readSource(com.actisec.clipcaster.test.R.raw.lastpass_v3); + JavaScript javaScript = new JavaScript(source.javascriptProgram); + String lxfunc = javaScript.getFunction("l_x"); + assertNotNull(lxfunc); + assertEquals("l_x=function(t,l,m){ var o=[]; var b=''; var p=document.location.href.replace(/https?:\\/\\//, '').substring(0,l); p=l_s(''+l_f(m)+p); for (z=1; z<=255; z++){o[String.fromCharCode(z)]=z;} for (j=z=0; z p1CloseFuture = SettableFuture.create(); + peerOf(p1).addEventListener(new AbstractPeerEventListener() { + @Override + public void onPeerDisconnected(Peer peer, int peerCount) { + p1CloseFuture.set(null); + } + }); + closePeer(peerOf(p1)); + p1CloseFuture.get(); + // Peer 2 fetches it next time it hears an inv (should it fetch immediately?). + inbound(p2, inv); + assertTrue(outbound(p2) instanceof GetDataMessage); + } + + @Test + public void singleDownloadPeer2() throws Exception { + // Check that we don't attempt multiple simultaneous block chain downloads, when adding a new peer in the + // middle of an existing chain download. + // Create a couple of peers. + peerGroup.start(); + + // Create a couple of peers. + InboundMessageQueuer p1 = connectPeer(1); + + // Set up a little block chain. + Block b1 = FakeTxBuilder.createFakeBlock(blockStore).block; + Block b2 = FakeTxBuilder.makeSolvedTestBlock(b1); + Block b3 = FakeTxBuilder.makeSolvedTestBlock(b2); + + // Expect a zero hash getblocks on p1. This is how the process starts. + peerGroup.startBlockChainDownload(new AbstractPeerEventListener() { + }); + GetBlocksMessage getblocks = (GetBlocksMessage) outbound(p1); + assertEquals(Sha256Hash.ZERO_HASH, getblocks.getStopHash()); + // We give back an inv with some blocks in it. + InventoryMessage inv = new InventoryMessage(params); + inv.addBlock(b1); + inv.addBlock(b2); + inv.addBlock(b3); + + inbound(p1, inv); + assertTrue(outbound(p1) instanceof GetDataMessage); + // We hand back the first block. + inbound(p1, b1); + // Now we successfully connect to another peer. There should be no messages sent. + InboundMessageQueuer p2 = connectPeer(2); + Message message = (Message)outbound(p2); + assertNull(message == null ? "" : message.toString(), message); + } + + @Test + public void transactionConfidence() throws Exception { + // Checks that we correctly count how many peers broadcast a transaction, so we can establish some measure of + // its trustworthyness assuming an untampered with internet connection. + peerGroup.start(); + + final Transaction[] event = new Transaction[1]; + final TransactionConfidence[] confEvent = new TransactionConfidence[1]; + peerGroup.addEventListener(new AbstractPeerEventListener() { + @Override + public void onTransaction(Peer peer, Transaction t) { + event[0] = t; + } + }, Threading.SAME_THREAD); + + InboundMessageQueuer p1 = connectPeer(1); + InboundMessageQueuer p2 = connectPeer(2); + InboundMessageQueuer p3 = connectPeer(3); + + Transaction tx = FakeTxBuilder.createFakeTx(params, valueOf(20, 0), address); + InventoryMessage inv = new InventoryMessage(params); + inv.addTransaction(tx); + + // Peer 2 advertises the tx but does not receive it yet. + inbound(p2, inv); + assertTrue(outbound(p2) instanceof GetDataMessage); + assertEquals(0, tx.getConfidence().numBroadcastPeers()); + assertTrue(blockChain.getContext().getConfidenceTable().maybeWasSeen(tx.getHash())); + assertNull(event[0]); + // Peer 1 advertises the tx, we don't do anything as it's already been requested. + inbound(p1, inv); + assertNull(outbound(p1)); + // Peer 2 gets sent the tx and requests the dependency. + inbound(p2, tx); + assertTrue(outbound(p2) instanceof GetDataMessage); + tx = event[0]; // We want to use the canonical copy delivered by the PeerGroup from now on. + assertNotNull(tx); + event[0] = null; + + // Peer 1 (the download peer) advertises the tx, we download it. + inbound(p1, inv); // returns getdata + inbound(p1, tx); // returns nothing after a queue drain. + // Two peers saw this tx hash. + assertEquals(2, tx.getConfidence().numBroadcastPeers()); + assertTrue(tx.getConfidence().wasBroadcastBy(peerOf(p1).getAddress())); + assertTrue(tx.getConfidence().wasBroadcastBy(peerOf(p2).getAddress())); + + tx.getConfidence().addEventListener(new TransactionConfidence.Listener() { + @Override + public void onConfidenceChanged(TransactionConfidence confidence, TransactionConfidence.Listener.ChangeReason reason) { + confEvent[0] = confidence; + } + }); + // A straggler reports in. + inbound(p3, inv); + pingAndWait(p3); + Threading.waitForUserCode(); + assertEquals(tx.getHash(), confEvent[0].getTransactionHash()); + assertEquals(3, tx.getConfidence().numBroadcastPeers()); + assertTrue(tx.getConfidence().wasBroadcastBy(peerOf(p3).getAddress())); + } + + @Test + public void testWalletCatchupTime() throws Exception { + // Check the fast catchup time was initialized to something around the current runtime minus a week. + // The wallet was already added to the peer in setup. + final int WEEK = 86400 * 7; + final long now = Utils.currentTimeSeconds(); + peerGroup.start(); + assertTrue(peerGroup.getFastCatchupTimeSecs() > now - WEEK - 10000); + Wallet w2 = new Wallet(params); + ECKey key1 = new ECKey(); + key1.setCreationTimeSeconds(now - 86400); // One day ago. + w2.importKey(key1); + peerGroup.addWallet(w2); + peerGroup.waitForJobQueue(); + assertEquals(peerGroup.getFastCatchupTimeSecs(), now - 86400 - WEEK); + // Adding a key to the wallet should update the fast catchup time, but asynchronously and in the background + // due to the need to avoid complicated lock inversions. + ECKey key2 = new ECKey(); + key2.setCreationTimeSeconds(now - 100000); + w2.importKey(key2); + peerGroup.waitForJobQueue(); + assertEquals(peerGroup.getFastCatchupTimeSecs(), now - WEEK - 100000); + } + + @Test + public void noPings() throws Exception { + peerGroup.start(); + peerGroup.setPingIntervalMsec(0); + VersionMessage versionMessage = new VersionMessage(params, 2); + versionMessage.clientVersion = FilteredBlock.MIN_PROTOCOL_VERSION; + versionMessage.localServices = VersionMessage.NODE_NETWORK; + connectPeer(1, versionMessage); + peerGroup.waitForPeers(1).get(); + assertFalse(peerGroup.getConnectedPeers().get(0).getLastPingTime() < Long.MAX_VALUE); + } + + @Test + public void pings() throws Exception { + peerGroup.start(); + peerGroup.setPingIntervalMsec(100); + VersionMessage versionMessage = new VersionMessage(params, 2); + versionMessage.clientVersion = FilteredBlock.MIN_PROTOCOL_VERSION; + versionMessage.localServices = VersionMessage.NODE_NETWORK; + InboundMessageQueuer p1 = connectPeer(1, versionMessage); + Ping ping = (Ping) waitForOutbound(p1); + inbound(p1, new Pong(ping.getNonce())); + pingAndWait(p1); + assertTrue(peerGroup.getConnectedPeers().get(0).getLastPingTime() < Long.MAX_VALUE); + // The call to outbound should block until a ping arrives. + ping = (Ping) waitForOutbound(p1); + inbound(p1, new Pong(ping.getNonce())); + assertTrue(peerGroup.getConnectedPeers().get(0).getLastPingTime() < Long.MAX_VALUE); + } + + @Test + public void downloadPeerSelection() throws Exception { + peerGroup.start(); + VersionMessage versionMessage2 = new VersionMessage(params, 2); + versionMessage2.clientVersion = FilteredBlock.MIN_PROTOCOL_VERSION; + versionMessage2.localServices = VersionMessage.NODE_NETWORK; + VersionMessage versionMessage3 = new VersionMessage(params, 3); + versionMessage3.clientVersion = FilteredBlock.MIN_PROTOCOL_VERSION; + versionMessage3.localServices = VersionMessage.NODE_NETWORK; + assertNull(peerGroup.getDownloadPeer()); + Peer a = connectPeer(1, versionMessage2).peer; + assertEquals(2, peerGroup.getMostCommonChainHeight()); + assertEquals(a, peerGroup.getDownloadPeer()); + connectPeer(2, versionMessage2); + assertEquals(2, peerGroup.getMostCommonChainHeight()); + assertEquals(a, peerGroup.getDownloadPeer()); // No change. + Peer c = connectPeer(3, versionMessage3).peer; + assertEquals(2, peerGroup.getMostCommonChainHeight()); + assertEquals(a, peerGroup.getDownloadPeer()); // No change yet. + connectPeer(4, versionMessage3); + assertEquals(3, peerGroup.getMostCommonChainHeight()); + assertEquals(a, peerGroup.getDownloadPeer()); // Still no change. + + // New peer with a higher protocol version but same chain height. + // TODO: When PeerGroup.selectDownloadPeer.PREFERRED_VERSION is not equal to vMinRequiredProtocolVersion, + // reenable this test + /*VersionMessage versionMessage4 = new VersionMessage(params, 3); + versionMessage4.clientVersion = 100000; + versionMessage4.localServices = VersionMessage.NODE_NETWORK; + InboundMessageQueuer d = connectPeer(5, versionMessage4); + assertEquals(d.peer, peerGroup.getDownloadPeer());*/ + } + + @Test + public void peerTimeoutTest() throws Exception { + peerGroup.start(); + peerGroup.setConnectTimeoutMillis(100); + + final SettableFuture peerConnectedFuture = SettableFuture.create(); + final SettableFuture peerDisconnectedFuture = SettableFuture.create(); + peerGroup.addEventListener(new AbstractPeerEventListener() { + @Override + public void onPeerConnected(Peer peer, int peerCount) { + peerConnectedFuture.set(null); + } + + @Override + public void onPeerDisconnected(Peer peer, int peerCount) { + peerDisconnectedFuture.set(null); + } + }, Threading.SAME_THREAD); + connectPeerWithoutVersionExchange(0); + Thread.sleep(50); + assertFalse(peerConnectedFuture.isDone() || peerDisconnectedFuture.isDone()); + Thread.sleep(60); + assertTrue(!peerConnectedFuture.isDone()); + assertTrue(!peerConnectedFuture.isDone() && peerDisconnectedFuture.isDone()); + } + + @Test + @Ignore("disabled for now as this test is too flaky") + public void peerPriority() throws Exception { + final List addresses = Lists.newArrayList( + new InetSocketAddress("localhost", 2000), + new InetSocketAddress("localhost", 2001), + new InetSocketAddress("localhost", 2002) + ); + peerGroup.addEventListener(listener); + peerGroup.addPeerDiscovery(new PeerDiscovery() { + @Override + public InetSocketAddress[] getPeers(long unused, TimeUnit unused2) throws PeerDiscoveryException { + return addresses.toArray(new InetSocketAddress[addresses.size()]); + } + + @Override + public void shutdown() { + } + }); + peerGroup.setMaxConnections(3); + + Utils.setMockSleep(true); + blockJobs = true; + + jobBlocks.release(2); // startup + first peer discovery + peerGroup.start(); + + jobBlocks.release(3); // One for each peer. + handleConnectToPeer(0); + handleConnectToPeer(1); + handleConnectToPeer(2); + connectedPeers.take(); + connectedPeers.take(); + connectedPeers.take(); + addresses.clear(); + addresses.addAll(Lists.newArrayList(new InetSocketAddress("localhost", 2003))); + stopPeerServer(2); + assertEquals(2002, disconnectedPeers.take().getAddress().getPort()); // peer died + + // discovers, connects to new peer + jobBlocks.release(1); + handleConnectToPeer(3); + assertEquals(2003, connectedPeers.take().getAddress().getPort()); + + stopPeerServer(1); + assertEquals(2001, disconnectedPeers.take().getAddress().getPort()); // peer died + + // Alternates trying two offline peers + jobBlocks.release(10); + assertEquals(2001, disconnectedPeers.take().getAddress().getPort()); + assertEquals(2002, disconnectedPeers.take().getAddress().getPort()); + assertEquals(2001, disconnectedPeers.take().getAddress().getPort()); + assertEquals(2002, disconnectedPeers.take().getAddress().getPort()); + assertEquals(2001, disconnectedPeers.take().getAddress().getPort()); + + // Peer 2 comes online + startPeerServer(2); + jobBlocks.release(1); + handleConnectToPeer(2); + assertEquals(2002, connectedPeers.take().getAddress().getPort()); + + jobBlocks.release(6); + stopPeerServer(2); + assertEquals(2002, disconnectedPeers.take().getAddress().getPort()); // peer died + + // Peer 2 is tried before peer 1, since it has a lower backoff due to recent success + assertEquals(2002, disconnectedPeers.take().getAddress().getPort()); + assertEquals(2001, disconnectedPeers.take().getAddress().getPort()); + } + + @Test + public void testBloomOnP2Pubkey() throws Exception { + // Cover bug 513. When a relevant transaction with a p2pubkey output is found, the Bloom filter should be + // recalculated to include that transaction hash but not re-broadcast as the remote nodes should have followed + // the same procedure. However a new node that's connected should get the fresh filter. + peerGroup.start(); + final ECKey key = wallet.currentReceiveKey(); + // Create a couple of peers. + InboundMessageQueuer p1 = connectPeer(1); + InboundMessageQueuer p2 = connectPeer(2); + // Create a pay to pubkey tx. + Transaction tx = FakeTxBuilder.createFakeTx(params, COIN, key); + Transaction tx2 = new Transaction(params); + tx2.addInput(tx.getOutput(0)); + TransactionOutPoint outpoint = tx2.getInput(0).getOutpoint(); + assertTrue(p1.lastReceivedFilter.contains(key.getPubKey())); + assertFalse(p1.lastReceivedFilter.contains(tx.getHash().getBytes())); + inbound(p1, tx); + // p1 requests dep resolution, p2 is quiet. + assertTrue(outbound(p1) instanceof GetDataMessage); + final Sha256Hash dephash = tx.getInput(0).getOutpoint().getHash(); + final InventoryItem inv = new InventoryItem(InventoryItem.Type.Transaction, dephash); + inbound(p1, new NotFoundMessage(params, ImmutableList.of(inv))); + assertNull(outbound(p1)); + assertNull(outbound(p2)); + peerGroup.waitForJobQueue(); + // Now we connect p3 and there is a new bloom filter sent, that DOES match the relevant outpoint. + InboundMessageQueuer p3 = connectPeer(3); + assertTrue(p3.lastReceivedFilter.contains(key.getPubKey())); + assertTrue(p3.lastReceivedFilter.contains(outpoint.bitcoinSerialize())); + } + + @Test + public void testBloomResendOnNewKey() throws Exception { + // Check that when we add a new key to the wallet, the Bloom filter is re-calculated and re-sent but only once + // we exceed the lookahead threshold. + wallet.setKeychainLookaheadSize(5); + wallet.setKeychainLookaheadThreshold(4); + peerGroup.start(); + // Create a couple of peers. + InboundMessageQueuer p1 = connectPeer(1); + InboundMessageQueuer p2 = connectPeer(2); + peerGroup.waitForJobQueue(); + BloomFilter f1 = p1.lastReceivedFilter; + ECKey key = null; + // We have to run ahead of the lookahead zone for this test. There should only be one bloom filter recalc. + for (int i = 0; i < wallet.getKeychainLookaheadSize() + wallet.getKeychainLookaheadThreshold() + 1; i++) { + key = wallet.freshReceiveKey(); + } + peerGroup.waitForJobQueue(); + BloomFilter bf, f2 = null; + while ((bf = (BloomFilter) outbound(p1)) != null) { + assertEquals(MemoryPoolMessage.class, outbound(p1).getClass()); + f2 = bf; + } + assertNotNull(key); + assertNotNull(f2); + assertNull(outbound(p1)); + // Check the last filter received. + assertNotEquals(f1, f2); + assertTrue(f2.contains(key.getPubKey())); + assertTrue(f2.contains(key.getPubKeyHash())); + assertFalse(f1.contains(key.getPubKey())); + assertFalse(f1.contains(key.getPubKeyHash())); + } + + @Test + public void waitForNumPeers1() throws Exception { + ListenableFuture> future = peerGroup.waitForPeers(3); + peerGroup.start(); + assertFalse(future.isDone()); + connectPeer(1); + assertFalse(future.isDone()); + connectPeer(2); + assertFalse(future.isDone()); + assertTrue(peerGroup.waitForPeers(2).isDone()); // Immediate completion. + connectPeer(3); + future.get(); + assertTrue(future.isDone()); + } + + @Test + public void waitForPeersOfVersion() throws Exception { + final int baseVer = peerGroup.getMinRequiredProtocolVersion() + 3000; + final int newVer = baseVer + 1000; + + ListenableFuture> future = peerGroup.waitForPeersOfVersion(2, newVer); + + VersionMessage ver1 = new VersionMessage(params, 10); + ver1.clientVersion = baseVer; + ver1.localServices = VersionMessage.NODE_NETWORK; + VersionMessage ver2 = new VersionMessage(params, 10); + ver2.clientVersion = newVer; + ver2.localServices = VersionMessage.NODE_NETWORK; + peerGroup.start(); + assertFalse(future.isDone()); + connectPeer(1, ver1); + assertFalse(future.isDone()); + connectPeer(2, ver2); + assertFalse(future.isDone()); + assertTrue(peerGroup.waitForPeersOfVersion(1, newVer).isDone()); // Immediate completion. + connectPeer(3, ver2); + future.get(); + assertTrue(future.isDone()); + } + + @Test + public void waitForPeersWithServiceFlags() throws Exception { + ListenableFuture> future = peerGroup.waitForPeersWithServiceMask(2, 3); + + VersionMessage ver1 = new VersionMessage(params, 10); + ver1.clientVersion = 70000; + ver1.localServices = VersionMessage.NODE_NETWORK; + VersionMessage ver2 = new VersionMessage(params, 10); + ver2.clientVersion = 70000; + ver2.localServices = VersionMessage.NODE_NETWORK | 2; + peerGroup.start(); + assertFalse(future.isDone()); + connectPeer(1, ver1); + assertTrue(peerGroup.findPeersWithServiceMask(3).isEmpty()); + assertFalse(future.isDone()); + connectPeer(2, ver2); + assertFalse(future.isDone()); + assertEquals(1, peerGroup.findPeersWithServiceMask(3).size()); + assertTrue(peerGroup.waitForPeersWithServiceMask(1, 0x3).isDone()); // Immediate completion. + connectPeer(3, ver2); + future.get(); + assertTrue(future.isDone()); + peerGroup.stop(); + } + + @Test + public void preferLocalPeer() throws IOException { + // Because we are using the same port (8333 or 18333) that is used by Satoshi client + // We have to consider 2 cases: + // 1. Test are executed on the same machine that is running full node / Satoshi client + // 2. Test are executed without any full node running locally + // We have to avoid to connecting to real and external services in unit tests + // So we skip this test in case we have already something running on port params.getPort() + + // Check that if we have a localhost port 8333 or 18333 then it's used instead of the p2p network. + ServerSocket local = null; + try { + local = new ServerSocket(params.getPort(), 100, InetAddresses.forString("127.0.0.1")); + } + catch(BindException e) { // Port already in use, skipping this test. + return; + } + + try { + peerGroup.setUseLocalhostPeerWhenPossible(true); + peerGroup.start(); + local.accept().close(); // Probe connect + local.accept(); // Real connect + // If we get here it used the local peer. Check no others are in use. + assertEquals(1, peerGroup.getMaxConnections()); + assertEquals(PeerAddress.localhost(params), peerGroup.getPendingPeers().get(0).getAddress()); + } finally { + local.close(); + } + } + + private T assertNextMessageIs(InboundMessageQueuer q, Class klass) throws Exception { + Message outbound = waitForOutbound(q); + assertEquals(klass, outbound.getClass()); + return (T) outbound; + } + + @Test + public void autoRescanOnKeyExhaustion() throws Exception { + // Check that if the last key that was inserted into the bloom filter is seen in some requested blocks, + // that the exhausting block is discarded, a new filter is calculated and sent, and then the download resumes. + + final int NUM_KEYS = 9; + + // First, grab a load of keys from the wallet, and then recreate it so it forgets that those keys were issued. + Wallet shadow = Wallet.fromSeed(wallet.getParams(), wallet.getKeyChainSeed()); + List keys = new ArrayList(NUM_KEYS); + for (int i = 0; i < NUM_KEYS; i++) { + keys.add(shadow.freshReceiveKey()); + } + // Reduce the number of keys we need to work with to speed up this test. + wallet.setKeychainLookaheadSize(4); + wallet.setKeychainLookaheadThreshold(2); + + peerGroup.start(); + InboundMessageQueuer p1 = connectPeer(1); + assertTrue(p1.lastReceivedFilter.contains(keys.get(0).getPubKey())); + assertTrue(p1.lastReceivedFilter.contains(keys.get(5).getPubKeyHash())); + assertFalse(p1.lastReceivedFilter.contains(keys.get(keys.size() - 1).getPubKey())); + peerGroup.startBlockChainDownload(null); + assertNextMessageIs(p1, GetBlocksMessage.class); + + // Make some transactions and blocks that send money to the wallet thus using up all the keys. + List blocks = Lists.newArrayList(); + Coin expectedBalance = Coin.ZERO; + Block prev = blockStore.getChainHead().getHeader(); + for (ECKey key1 : keys) { + Address addr = key1.toAddress(params); + Block next = FakeTxBuilder.makeSolvedTestBlock(prev, FakeTxBuilder.createFakeTx(params, Coin.FIFTY_COINS, addr)); + expectedBalance = expectedBalance.add(next.getTransactions().get(2).getOutput(0).getValue()); + blocks.add(next); + prev = next; + } + + // Send the chain that doesn't have all the transactions in it. The blocks after the exhaustion point should all + // be ignored. + int epoch = wallet.keychain.getCombinedKeyLookaheadEpochs(); + BloomFilter filter = new BloomFilter(params, p1.lastReceivedFilter.bitcoinSerialize()); + filterAndSend(p1, blocks, filter); + Block exhaustionPoint = blocks.get(3); + pingAndWait(p1); + + assertNotEquals(epoch, wallet.keychain.getCombinedKeyLookaheadEpochs()); + // 4th block was end of the lookahead zone and thus was discarded, so we got 3 blocks worth of money (50 each). + assertEquals(Coin.FIFTY_COINS.multiply(3), wallet.getBalance()); + assertEquals(exhaustionPoint.getPrevBlockHash(), blockChain.getChainHead().getHeader().getHash()); + + // Await the new filter. + peerGroup.waitForJobQueue(); + BloomFilter newFilter = assertNextMessageIs(p1, BloomFilter.class); + assertNotEquals(filter, newFilter); + assertNextMessageIs(p1, MemoryPoolMessage.class); + Ping ping = assertNextMessageIs(p1, Ping.class); + inbound(p1, new Pong(ping.getNonce())); + + // Await restart of the chain download. + GetDataMessage getdata = assertNextMessageIs(p1, GetDataMessage.class); + assertEquals(exhaustionPoint.getHash(), getdata.getHashOf(0)); + assertEquals(InventoryItem.Type.FilteredBlock, getdata.getItems().get(0).type); + List newBlocks = blocks.subList(3, blocks.size()); + filterAndSend(p1, newBlocks, newFilter); + assertNextMessageIs(p1, Ping.class); + + // It happened again. + peerGroup.waitForJobQueue(); + newFilter = assertNextMessageIs(p1, BloomFilter.class); + assertNextMessageIs(p1, MemoryPoolMessage.class); + inbound(p1, new Pong(assertNextMessageIs(p1, Ping.class).getNonce())); + assertNextMessageIs(p1, GetDataMessage.class); + newBlocks = blocks.subList(6, blocks.size()); + filterAndSend(p1, newBlocks, newFilter); + // Send a non-tx message so the peer knows the filtered block is over and force processing. + inbound(p1, new Ping()); + pingAndWait(p1); + + assertEquals(expectedBalance, wallet.getBalance()); + assertEquals(blocks.get(blocks.size() - 1).getHash(), blockChain.getChainHead().getHeader().getHash()); + } + + private void filterAndSend(InboundMessageQueuer p1, List blocks, BloomFilter filter) { + for (Block block : blocks) { + FilteredBlock fb = filter.applyAndUpdate(block); + inbound(p1, fb); + for (Transaction tx : fb.getAssociatedTransactions().values()) + inbound(p1, tx); + } + } +} diff --git a/src/resources/LazyTest/src/main/java/com/github/marmaladesky/Cryptographer.java b/src/resources/LazyTest/src/main/java/com/github/marmaladesky/Cryptographer.java new file mode 100644 index 0000000..5ba905f --- /dev/null +++ b/src/resources/LazyTest/src/main/java/com/github/marmaladesky/Cryptographer.java @@ -0,0 +1,157 @@ +package com.github.marmaladesky; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; +import java.io.*; +import java.nio.charset.Charset; +import java.security.Key; +import java.security.MessageDigest; +import java.security.spec.KeySpec; +import java.util.Arrays; +import java.util.Random; +import java.util.zip.DataFormatException; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.Inflater; + +public class Cryptographer { + + final static byte[] MAGIC_STRING_DATA_VERSION_2 = new byte[] {'r', 'v', 'l', 0, 2, 0}; + final static byte[] VERSION_0_4_7 = new byte[] {0, 4, 7}; + + public static String decrypt(byte[] fileData, String password) throws Exception { + byte[] header; + header = Arrays.copyOfRange(fileData, 0, 36); + + byte[] iv = null; + byte[] salt = null; + + if(Arrays.equals(Arrays.copyOfRange(header, 0, 6), MAGIC_STRING_DATA_VERSION_2)) { + if(Arrays.equals(VERSION_0_4_7, Arrays.copyOfRange(header, 6, 9))) { + salt = Arrays.copyOfRange(header, 12, 20); + iv = Arrays.copyOfRange(header, 20, 36); + Cipher cypher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + SecretKeyFactory scf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); + KeySpec ks = new PBEKeySpec(password.toCharArray(), salt, 12000, 256); + SecretKey s = scf.generateSecret(ks); // Bottleneck (12k) + Key k = new SecretKeySpec(s.getEncoded(),"AES"); + cypher.init(Cipher.DECRYPT_MODE, k, new IvParameterSpec(iv)); + + byte[] input = Arrays.copyOfRange(fileData, 36, fileData.length); + byte[] compressedData = cypher.doFinal(input); + + byte[] hash256 = Arrays.copyOfRange(compressedData, 0, 32); + + compressedData = Arrays.copyOfRange(compressedData, 32, compressedData.length); + + compressedData = addPadding(compressedData); + + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(compressedData); + byte[] computedHash = md.digest(); + + if(!Arrays.equals(computedHash, hash256)) { + throw new Exception("Invalid data"); + } + + byte[] result = decompress(compressedData); + return new String(result, Charset.forName("UTF-8")); + } + } + + throw new Exception("Unknown file format"); + } + + public static byte[] encrypt(String xmlData, String password) throws Exception { + if(password == null || password.equals("")) + throw new Exception("Password cannot be empty"); + + Random r = new Random(); + + byte[] salt = new byte[8]; + r.nextBytes(salt); + + byte[] compressedData = compress(xmlData); + + byte[] compressedDataWithPadlen = addPadding(compressedData); + + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(compressedDataWithPadlen); + byte[] computedHash = md.digest(); + + byte[] hashAndData = concatenateByteArrays(computedHash, compressedData); + + byte[] iv = new byte[16]; + r.nextBytes(iv); + + Cipher cypher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + + SecretKeyFactory scf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); + + KeySpec ks = new PBEKeySpec(password.toCharArray(), salt, 12000, 256); + SecretKey s = scf.generateSecret(ks); + Key k = new SecretKeySpec(s.getEncoded(), "AES"); + + cypher.init(Cipher.ENCRYPT_MODE, k, new IvParameterSpec(iv)); + + byte[] encrypted= new byte[cypher.getOutputSize(hashAndData.length)]; + + int enc_len = cypher.update(hashAndData, 0, hashAndData.length, encrypted, 0); + cypher.doFinal(encrypted, enc_len); + + byte[] a1 = concatenateByteArrays(MAGIC_STRING_DATA_VERSION_2, VERSION_0_4_7); + a1 = concatenateByteArrays(a1, new byte[] {0,0,0}); + a1 = concatenateByteArrays(a1, salt); + a1 = concatenateByteArrays(a1, iv); + a1 = concatenateByteArrays(a1, encrypted); + + return a1; + } + + private static byte[] decompress(byte[] inputData) throws DataFormatException, IOException { + Inflater decompressor = new Inflater(); + decompressor.setInput(inputData); + ByteArrayOutputStream bos = new ByteArrayOutputStream(inputData.length); + byte[] buf = new byte[1024]; + while (!decompressor.finished()) { + int count = decompressor.inflate(buf); + bos.write(buf, 0, count); + } + bos.close(); + return bos.toByteArray(); + } + + private static byte[] compress(String text) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + OutputStream out = new DeflaterOutputStream(baos); + out.write(text.getBytes("UTF-8")); + out.close(); + } catch (IOException e) { + throw new AssertionError(e); + } + return baos.toByteArray(); + } + + private static byte[] concatenateByteArrays(byte[] a, byte[] b) { + byte[] result = new byte[a.length + b.length]; + System.arraycopy(a, 0, result, 0, a.length); + System.arraycopy(b, 0, result, a.length, b.length); + return result; + } + + private static byte[] addPadding (byte[] a) { + byte padlen = (byte)(16 - (a.length % 16)); + if (padlen == 0) + padlen = 16; + + byte[] b = new byte[a.length + padlen]; + System.arraycopy(a, 0, b, 0, a.length); + for (int i = 0; i < padlen; i++) + b[a.length + i] = padlen; + return b; + } +} diff --git a/src/resources/LazyTest/src/test/java/com/github/marmaladesky/tests/CryptographerTest.java b/src/resources/LazyTest/src/test/java/com/github/marmaladesky/tests/CryptographerTest.java new file mode 100644 index 0000000..fe091b0 --- /dev/null +++ b/src/resources/LazyTest/src/test/java/com/github/marmaladesky/tests/CryptographerTest.java @@ -0,0 +1,51 @@ +package com.github.marmaladesky.tests; + +import com.github.marmaladesky.Cryptographer; + +import org.junit.Test; + +import java.io.*; + +import static org.junit.Assert.assertEquals; + +public class CryptographerTest { + + private static final String DECRYPTED_DATA_FILE_4_14 = "src/test/res/rvl_test-0.4.14.xml"; + private static final String ENCRYPTED_DATA_FILE_4_14 = "src/test/res/rvl_test-0.4.14"; + + @Test + public void testDecrypt() throws Exception { + FileInputStream file = new FileInputStream(ENCRYPTED_DATA_FILE_4_14); + byte[] enfileData = new byte[file.available()]; + FileInputStream input = new FileInputStream(DECRYPTED_DATA_FILE_4_14); + byte[] fileData = new byte[input.available()]; + input.read(fileData); + input.close(); + file.read(enfileData); + file.close(); + String expectedResult = new String(fileData, "UTF-8"); + assertEquals("Testing simple decrypt",expectedResult, Cryptographer.decrypt(enfileData, "test")); + } + + @Test + public void testEncrypt() throws Exception { + String xml = readFileAsString(DECRYPTED_DATA_FILE_4_14); + byte[] encrypted = Cryptographer.encrypt(xml, "test"); + String decrypt = Cryptographer.decrypt(encrypted, "test"); + assertEquals(xml, decrypt); + } + + private String readFileAsString(String filePath) throws IOException { + StringBuffer fileData = new StringBuffer(); + BufferedReader reader = new BufferedReader( + new FileReader(filePath)); + char[] buf = new char[1024]; + int numRead; + while((numRead=reader.read(buf)) != -1){ + String readData = String.valueOf(buf, 0, numRead); + fileData.append(readData); + } + reader.close(); + return fileData.toString(); + } +} diff --git a/src/resources/META-INF/MANIFEST.MF b/src/resources/META-INF/MANIFEST.MF index 5ee19cb..6711b7a 100644 --- a/src/resources/META-INF/MANIFEST.MF +++ b/src/resources/META-INF/MANIFEST.MF @@ -1,3 +1,3 @@ -Manifest-Version: 1.0 -Main-Class: Main - +Manifest-Version: 1.0 +Main-Class: edu.rit.se.testsmells.Main + diff --git a/src/resources/MagicNumberTest/src/test/java/com/luckycatlabs/sunrisesunset/calculator/SolarEventCalculatorTest.java b/src/resources/MagicNumberTest/src/test/java/com/luckycatlabs/sunrisesunset/calculator/SolarEventCalculatorTest.java new file mode 100644 index 0000000..9423880 --- /dev/null +++ b/src/resources/MagicNumberTest/src/test/java/com/luckycatlabs/sunrisesunset/calculator/SolarEventCalculatorTest.java @@ -0,0 +1,72 @@ +/* + * Copyright 2008-2009 Mike Reedell / LuckyCatLabs. + * + * 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 com.luckycatlabs.sunrisesunset.calculator; + +import static org.junit.Assert.assertEquals; + +import java.math.BigDecimal; +import java.util.Calendar; + +import org.junit.Before; +import org.junit.Test; + +import com.luckycatlabs.sunrisesunset.Zenith; +import com.luckycatlabs.sunrisesunset.util.BaseTestCase; + +public class SolarEventCalculatorTest extends BaseTestCase { + + private SolarEventCalculator calc; + + @Before + public void setupCalculator() { + super.setup(10, 1, 2008); + calc = new SolarEventCalculator(location, "America/New_York"); + } + + @Test + public void testComputeSunriseTime() { + String localSunriseTime = "07:05"; + assertEquals(localSunriseTime, calc.computeSunriseTime(Zenith.CIVIL, eventDate)); + } + + @Test + public void testComputeSunsetTime() { + String localSunsetTime = "18:28"; + assertEquals(localSunsetTime, calc.computeSunsetTime(Zenith.CIVIL, eventDate)); + } + + @Test + public void testGetLocalTimeAsCalendar() { + Calendar localTime = calc.getLocalTimeAsCalendar(BigDecimal.valueOf(15.5D), Calendar.getInstance()); + assertEquals(15, localTime.get(Calendar.HOUR_OF_DAY)); + assertEquals(30, localTime.get(Calendar.MINUTE)); + } + + @Test + public void testGetLocalTimeAsCalendarForZero() { + Calendar localTime = calc.getLocalTimeAsCalendar(BigDecimal.valueOf(0.0D), Calendar.getInstance()); + assertEquals(0, localTime.get(Calendar.HOUR_OF_DAY)); + assertEquals(0, localTime.get(Calendar.MINUTE)); + } + + @Test + public void testGetLocalTimeAsCalendarForNegative() { + Calendar localTime = calc.getLocalTimeAsCalendar(BigDecimal.valueOf(-10.0D), Calendar.getInstance()); + assertEquals(14, localTime.get(Calendar.HOUR_OF_DAY)); + assertEquals(0, localTime.get(Calendar.MINUTE)); + } +} diff --git a/src/resources/MysteryGuest/src/androidTest/java/com/gmail/walles/johan/batterylogger/SystemStateTest.java b/src/resources/MysteryGuest/src/androidTest/java/com/gmail/walles/johan/batterylogger/SystemStateTest.java new file mode 100644 index 0000000..b9a3d2f --- /dev/null +++ b/src/resources/MysteryGuest/src/androidTest/java/com/gmail/walles/johan/batterylogger/SystemStateTest.java @@ -0,0 +1,276 @@ +/* + * Copyright 2016 Johan Walles + * + * 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 com.gmail.walles.johan.batterylogger; + +import android.test.AndroidTestCase; + +import java.io.File; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; + +public class SystemStateTest extends AndroidTestCase { + private final Date now = new Date(); + private final Date then = new Date(now.getTime() - History.FIVE_MINUTES_MS); + private final Date bootTimestamp = new Date(then.getTime() - History.FIVE_MINUTES_MS); + + public void testConstructor() { + try { + new SystemState(then, 27, false, now); + fail("Expected IAE when boot is in the future"); + } catch (IllegalArgumentException ignored) { + // Expected exception intentionally ignored + } + } + + public void testEquals() { + assertTrue(new SystemState(now, 27, true, bootTimestamp).equals(new SystemState(now, 27, true, bootTimestamp))); + assertFalse(new SystemState(now, 27, true, bootTimestamp).equals(new SystemState(then, 27, true, bootTimestamp))); + assertFalse(new SystemState(now, 27, true, bootTimestamp).equals(new SystemState(now, 36, true, bootTimestamp))); + assertFalse(new SystemState(now, 27, true, bootTimestamp).equals(new SystemState(now, 27, false, bootTimestamp))); + assertFalse(new SystemState(now, 27, true, bootTimestamp).equals(new SystemState(now, 27, false, then))); + + SystemState a = new SystemState(now, 27, false, bootTimestamp); + a.addInstalledApp("a.b.c", "Griseknoa", "1.2.3"); + + SystemState b = new SystemState(now, 27, false, bootTimestamp); + b.addInstalledApp("a.b.c", "Griseknoa", "1.2.3"); + assertEquals(a, b); + + SystemState c = new SystemState(now, 27, false, bootTimestamp); + c.addInstalledApp("x.y.z", "Griseknoa", "1.2.3"); + assertFalse(a.equals(c)); + + SystemState d = new SystemState(now, 27, false, bootTimestamp); + d.addInstalledApp("a.b.c", "Charles-Ingvar", "1.2.3"); + assertFalse(a.equals(d)); + + SystemState e = new SystemState(now, 27, false, bootTimestamp); + e.addInstalledApp("a.b.c", "Griseknoa", "4.5.6"); + assertFalse(a.equals(e)); + } + + public void testUnorderedEquals() { + SystemState a = new SystemState(now, 27, false, bootTimestamp); + a.addInstalledApp("a.b.c", "Griseknoa", "1.2.3"); + a.addInstalledApp("d.e.f", "Snickarboa", "4.5.6"); + + SystemState b = new SystemState(now, 27, false, bootTimestamp); + b.addInstalledApp("d.e.f", "Snickarboa", "4.5.6"); + b.addInstalledApp("a.b.c", "Griseknoa", "1.2.3"); + + assertEquals(a, b); + } + + private void assertEvents(Collection testMe, HistoryEvent ... expected) { + assertNotNull("Events must be non-null, were null", testMe); + String expectedString = Arrays.toString(expected); + String actualString = Arrays.toString(testMe.toArray()); + assertEquals(expectedString, actualString); + } + + private void assertNoEvents(Collection testMe) { + assertEvents(testMe); + } + + public void testBatteryEvent() { + SystemState a = new SystemState(then, 27, false, bootTimestamp); + + SystemState b = new SystemState(now, 26, false, bootTimestamp); + assertEvents(b.getEventsSince(a), HistoryEvent.createBatteryLevelEvent(now, 26)); + + SystemState c = new SystemState(now, 28, true, bootTimestamp); + assertEvents(c.getEventsSince(a), + HistoryEvent.createStartChargingEvent(between(then, now)), + HistoryEvent.createBatteryLevelEvent(now, 28)); + + SystemState d = new SystemState(now, 29, true, bootTimestamp); + assertNoEvents(d.getEventsSince(c)); + } + + public void testStartChargingEvent() { + SystemState a = new SystemState(then, 27, false, bootTimestamp); + SystemState b = new SystemState(now, 27, true, bootTimestamp); + + assertEvents(b.getEventsSince(a), HistoryEvent.createStartChargingEvent(between(then, now))); + } + + public void testStopChargingEvent() { + SystemState a = new SystemState(then, 27, true, bootTimestamp); + SystemState b = new SystemState(now, 27, false, bootTimestamp); + + assertEvents(b.getEventsSince(a), HistoryEvent.createStopChargingEvent(between(then, now))); + } + + private static Date between(Date t0, Date t1) { + return new Date((t0.getTime() + t1.getTime()) / 2); + } + + public void testInstallEvent() { + SystemState a = new SystemState(then, 27, false, bootTimestamp); + + SystemState b = new SystemState(now, 27, false, bootTimestamp); + b.addInstalledApp("a.b.c", "ABC", "1.2.3"); + + assertEvents(b.getEventsSince(a), HistoryEvent.createInfoEvent(between(then, now), "ABC 1.2.3 installed")); + } + + public void testUninstallEvent() { + SystemState a = new SystemState(then, 27, false, bootTimestamp); + a.addInstalledApp("a.b.c", "ABC", "1.2.3"); + + SystemState b = new SystemState(now, 27, false, bootTimestamp); + + assertEvents(b.getEventsSince(a), HistoryEvent.createInfoEvent(between(then, now), "ABC 1.2.3 uninstalled")); + } + + public void testUpgradeEvent() { + SystemState a = new SystemState(then, 27, false, bootTimestamp); + a.addInstalledApp("a.b.c", "ABC", "1.2.3"); + + SystemState b = new SystemState(now, 27, false, bootTimestamp); + b.addInstalledApp("a.b.c", "ABC", "2.3.4"); + + assertEvents(b.getEventsSince(a), HistoryEvent.createInfoEvent(between(then, now), "ABC upgraded from 1.2.3 to 2.3.4")); + } + + public void testPersistence() throws Exception { + File tempFile = File.createTempFile("systemstate-", ".txt"); + try { + SystemState a = new SystemState(then, 27, false, bootTimestamp); + a.addInstalledApp("a.b.c", "ABC", "1.2.3"); + + a.writeToFile(tempFile); + SystemState b = SystemState.readFromFile(tempFile); + + assertEquals(a, b); + } finally { + //noinspection ConstantConditions + if (tempFile != null) { + //noinspection ResultOfMethodCallIgnored + tempFile.delete(); + } + } + } + + public void testHaltAndBootEvents() { + Date boot1 = new Date(0); + Date sample1 = new Date(100000); + Date boot2 = new Date(200000); + Date sample2 = new Date(300000); + + SystemState beforeReboot = new SystemState(sample1, 27, false, boot1); + SystemState afterReboot = new SystemState(sample2, 27, false, boot2); + + assertEvents(afterReboot.getEventsSince(beforeReboot), + HistoryEvent.createSystemHaltingEvent(new Date(sample1.getTime() + 1)), + HistoryEvent.createSystemBootingEvent(boot2, false)); + } + + public void testHaltAndBootAndChargeEvents() { + Date boot1 = new Date(0); + Date sample1 = new Date(100000); + Date boot2 = new Date(200000); + Date sample2 = new Date(300000); + + SystemState beforeReboot = new SystemState(sample1, 27, false, boot1); + SystemState afterReboot = new SystemState(sample2, 27, true, boot2); + + assertEvents(afterReboot.getEventsSince(beforeReboot), + HistoryEvent.createSystemHaltingEvent(new Date(sample1.getTime() + 1)), + HistoryEvent.createSystemBootingEvent(boot2, true)); + } + + /** + * Verify that different events resulting in text in the graph don't overlap each other. + */ + public void testPreventOverlappingEvents() { + SystemState a = new SystemState(then, 27, true, bootTimestamp); + a.addInstalledApp("a.b.c", "Upgrader", "1.2.3"); + a.addInstalledApp("d.e.f", "Remover", "2.3.4"); + + SystemState b = new SystemState(now, 26, false, bootTimestamp); + b.addInstalledApp("a.b.c", "Upgrader", "1.2.5"); + b.addInstalledApp("g.h.i", "Adder", "5.6.7"); + + Date datesBetween[] = SystemState.between(then, now, 4); + // Note that the actual order here is arbitrary + assertEvents(b.getEventsSince(a), + HistoryEvent.createStopChargingEvent(datesBetween[0]), + HistoryEvent.createInfoEvent(datesBetween[1], "Adder 5.6.7 installed"), + HistoryEvent.createInfoEvent(datesBetween[2], "Remover 2.3.4 uninstalled"), + HistoryEvent.createInfoEvent(datesBetween[3], "Upgrader upgraded from 1.2.3 to 1.2.5"), + HistoryEvent.createBatteryLevelEvent(now, 26)); + } + + public void testBetween() { + Date dates[] = SystemState.between(then, now, 1); + assertEquals(1, dates.length); + assertEquals(between(then, now), dates[0]); + } + + /** + * Two SystemStates should be equal even if their boot timestamps are a little bit off. Since the boot timestamps + * are calculated will millisecond precision we need some margin of error. + */ + public void testBootTimeLeniency() { + SystemState a = new SystemState(now, 27, false, new Date(0)); + SystemState b = new SystemState(now, 27, false, new Date(10 * 1000)); + assertEquals(a, b); + + SystemState c = new SystemState(now, 27, false, new Date(100 * 1000)); + assertFalse(a.equals(c)); + } + + /** + * Sanity check the current system state. + */ + public void testReadFromSystem() throws Exception { + //noinspection ConstantConditions + SystemState testMe = SystemState.readFromSystem(getContext()); + + int appCount = testMe.getAppCount(); + assertTrue("App count is " + appCount, testMe.getAppCount() > 10); + + int batteryPercentage = testMe.getBatteryPercentage(); + assertTrue("Battery percentage is " + batteryPercentage + "%", testMe.getBatteryPercentage() >= 0); + + File tempFile = File.createTempFile("systemstate-", ".txt"); + try { + testMe.writeToFile(tempFile); + assertEquals(testMe, SystemState.readFromFile(tempFile)); + } finally { + //noinspection ConstantConditions + if (tempFile != null) { + //noinspection ResultOfMethodCallIgnored + tempFile.delete(); + } + } + } + + public void testGetBootTimestamp() throws Exception { + SystemState.getBootTimestamp(); + Date b0 = SystemState.getBootTimestamp(); + Thread.sleep(1000, 0); + Date b1 = SystemState.getBootTimestamp(); + + long dt = Math.abs(b1.getTime() - b0.getTime()); + assertTrue("Too much drift over one second: " + dt + "ms", dt < 200); + + assertTrue("Boot timestamp can't be in the future", b0.before(new Date())); + } +} diff --git a/src/resources/Queue/pom.xml b/src/resources/Queue/pom.xml new file mode 100644 index 0000000..2d3f5ab --- /dev/null +++ b/src/resources/Queue/pom.xml @@ -0,0 +1,110 @@ + + + 4.0.0 + + br.ufv.aserg.victorveloso + Queue + 1.0-SNAPSHOT + + UTF-8 + 1.8 + ${maven.compiler.source} + 5.6.2 + + + + org.junit.jupiter + junit-jupiter + ${junit.jupiter.version} + test + + + org.assertj + assertj-core + + 3.11.1 + test + + + + + + org.pitest + pitest-maven + 1.5.1 + + + br.* + + + br.* + + true + true + + DetailedCSV + XML + + ALL + 4 + + + + org.pitest + pitest-junit5-plugin + 0.12 + + + org.pitest + pitest-detailed-csv-plugin + 0.4-SNAPSHOT + + + + + maven-compiler-plugin + 3.8.1 + + 8 + 8 + + + + maven-surefire-plugin + 2.22.2 + + + org.jacoco + jacoco-maven-plugin + 0.8.2 + + + + prepare-agent + + + + + report + test + + report + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.2.0 + + package + + + + + + + \ No newline at end of file diff --git a/src/resources/Queue/src/main/java/br/ufmg/aserg/victorveloso/queue/Queue.java b/src/resources/Queue/src/main/java/br/ufmg/aserg/victorveloso/queue/Queue.java new file mode 100644 index 0000000..61a96a4 --- /dev/null +++ b/src/resources/Queue/src/main/java/br/ufmg/aserg/victorveloso/queue/Queue.java @@ -0,0 +1,198 @@ +package br.ufmg.aserg.victorveloso.queue; + +import java.util.Arrays; +import java.util.stream.IntStream; + +/** + * A queue implementation based on ring buffer and cursors. Hence, less "memory hungry" and less flexible. + * @param The type of queue's elements + */ +public class Queue { + private T[] elements; + private int start; + private int end; + + public Queue(final T[] elements) { + this.elements = addTailNode(elements); + start = 0; + end = scanSize(elements); + } + + /** + * Static factory method for Queue of Integers initialization from built-in int[] type + * @param array Queue elements contained in an array of int + * @return Initialized Queue + */ + public static Queue fromIntArray(final int[] array) { + return new Queue<>(IntStream.of(array).boxed().toArray(Integer[]::new)); + } + + /** + * Get queue's current size + * @return The number of elements in queue + */ + public int size() { + return Math.floorMod((end - start), elements.length); + } + + /** + * Get queue's max size + * @return Queue's capacity + */ + public int capacity() { + return elements.length - 1; + } + + /** + * Checks for queue's emptiness + * @return Whether queue is empty + */ + public boolean isEmpty() { + return start == end; + } + + /** + * Checks for queue's fullness + * @return Whether queue is full + */ + public boolean isFull() { + return start == next(end); + } + + /** + * Check whether an object similar to "o" is contained in queue + * @param o Expected object + * @return Whether a similar object is found + */ + public boolean contains(T o) { + for (int i = start; i != end; i = next(i)) { + if (o.equals(elements[i])) { + return true; + } + } + return false; + } + + /** + * Push new element to the end of queue + * @param o New element + */ + public void enqueue(T o) { + if (isFull()) { + throw new ArrayIndexOutOfBoundsException("Queue is full"); + } + elements[end] = o; + end = next(end); + } + + /** + * Pops the first element from queue + * @return First element + */ + public T dequeue() { + if (isEmpty()) { + throw new ArrayIndexOutOfBoundsException("Queue is empty"); + } + T detached = elements[start]; + start = next(start); + return detached; + } + + /** + * Sort valid elements (non-nulls elements inside start-end range) + */ + public void sort() { + T[] values = dump(); + Arrays.sort(values); + for (T value : values) { + enqueue(value); + } + } + + /** + * Dequeue all valid elements preserving (FIFO) order + * @return Valid elements + */ + public T[] dump() { + T[] values = Arrays.copyOf(elements, size()); + for (int i = 0; i < values.length; i++) { + values[i] = dequeue(); + } + return values; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Queue)) return false; + + Queue queue = (Queue) o; + + if(size() != queue.size()) return false; + + return equalsDeeply(queue); + } + + /** + * Compare valid elements to another queue' valid elements + * @param queue Another queue to be compared with + * @return Whether both queues' elements are equal + */ + private boolean equalsDeeply(Queue queue) { + //Caution: dump has collateral effect + T[] ourElements = dump(); + T[] theirElements = queue.dump(); + //Compare + boolean isEqual = Arrays.equals(ourElements, theirElements); + //Restore previous state + for (int i = 0; i < ourElements.length; i++) { + enqueue(ourElements[i]); + queue.enqueue(theirElements[i]); + } + return isEqual; + } + + @Override + public int hashCode() { + return Arrays.hashCode(elements); + } + + @Override + public String toString() { + return "Queue{" + Arrays.toString(Arrays.copyOfRange(elements, start, end)) + '}'; + } + + /** + * Handle cyclic nature of Ring Buffer + * @param index An elements's cursor + * @return The next position on elements (a Ring Buffer) + */ + private int next(int index) { + return (index + 1) % elements.length; + } + + /** + * Calculates the distance (i.e. the "size" of the array) from start to last valid element + * @param elements Array of elements + * @return Size of elements + */ + private int scanSize(T[] elements) { + int lastValidElement = 0; + for (T element : elements) { + if (element == null) { + break; + } + lastValidElement = next(lastValidElement); + } + return lastValidElement; + } + + /** + * Adds a tail node allowing full state to be distinguishable from empty state + * @param elements Array of elements + * @return Array of elements concatenated to a null node + */ + private T[] addTailNode(T[] elements) { + return Arrays.copyOf(elements, elements.length + 1); + } +} diff --git a/src/resources/Queue/src/test/java/br/ufmg/aserg/victorveloso/queue/QueueTest.java b/src/resources/Queue/src/test/java/br/ufmg/aserg/victorveloso/queue/QueueTest.java new file mode 100644 index 0000000..ec4f38d --- /dev/null +++ b/src/resources/Queue/src/test/java/br/ufmg/aserg/victorveloso/queue/QueueTest.java @@ -0,0 +1,108 @@ +package br.ufmg.aserg.victorveloso.queue; + +import static org.assertj.core.api.Assertions.*; + +import com.sun.tools.javac.util.List; +import jdk.nashorn.internal.ir.annotations.Ignore; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +/** + * Test cases where Queue is initialized with an array of ints by the static factory method .fromIntArray + */ +class IntQueueTest { + private Queue sut; + private int[] array; + + @BeforeEach + void setUp() { + array = new int[]{23, 14, 55, 3, 0, -5}; + sut = Queue.fromIntArray(array); + } + + @Test + void testCapacity() { + assertThat(sut.capacity()).isEqualTo(array.length); + } + + @Test + void testSize() { + assertThat(sut.size()).isEqualTo(array.length); + } + + @Test + void testEmptiness() { + assertThat(sut.isEmpty()).isFalse(); + } + + @Test + void testEmptinessAfterDump() { + sut.dump(); + assertThat(sut.isEmpty()).isTrue(); + } + + @Test + void testEnqueueOnFull() { + assertThatExceptionOfType(ArrayIndexOutOfBoundsException.class).isThrownBy(() -> { + sut.enqueue(8); + }); + } + + @Test + void testToString() { + assertThat(sut.toString()).isEqualTo("Queue{[23, 14, 55, 3, 0, -5]}"); + } + + @Test + void testHashCode() { + assertThat(sut.hashCode()).isEqualTo(Arrays.hashCode(Arrays.copyOf(array, array.length + 1))); + } + + @Test + void testEqualitySameObject() { + assertThat(sut.equals(sut)).isTrue(); + } + + @Test + void testEqualityGeneralizedType() { + Object sameObject = sut; + + assertThat(sut.equals(sameObject)).isTrue(); + } + + @Test + void testEqualityOtherType() { + assertThat(sut.equals(array)).isFalse(); + } + + @Test + void testEqualityOtherContainedType() { + Queue queue = new Queue<>(new Long[]{1L, 2L, 3L, 4L}); + + assertThat(sut.equals(queue)).isFalse(); + } + + @Test + void testEqualitySmallerQueue() { + Queue smallerQueue = new Queue<>(new Integer[]{23, 14, 55, 3, 0}); + + assertThat(sut.equals(smallerQueue)).isFalse(); + } + + @Test + void testEqualitySlightlyDifferent() { + Queue slightlyDifferent = new Queue<>(new Integer[]{23, 14, 55, 3, 0, 5}); + + assertThat(sut.equals(slightlyDifferent)).isFalse(); + } + + @Test + void testEqualQueues() { + Queue equalQueue = new Queue<>(new Integer[]{23, 14, 55, 3, 0, -5}); + + assertThat(sut.equals(equalQueue)).isTrue(); + } + +} \ No newline at end of file diff --git a/src/resources/RedundantAssertion/src/androidTest/java/com/litmus/worldscope/LoginActivityTest.java b/src/resources/RedundantAssertion/src/androidTest/java/com/litmus/worldscope/LoginActivityTest.java new file mode 100644 index 0000000..bbd280f --- /dev/null +++ b/src/resources/RedundantAssertion/src/androidTest/java/com/litmus/worldscope/LoginActivityTest.java @@ -0,0 +1,41 @@ +package com.litmus.worldscope; + +import android.app.Instrumentation; +import android.support.test.InstrumentationRegistry; +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; + +import junit.framework.TestCase; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class LoginActivityTest extends TestCase{ + + private LoginActivity facebookLoginActivity; + + + @Rule + public ActivityTestRule activityRule = new ActivityTestRule<>( + LoginActivity.class); + + @Before + public void setUp() { + facebookLoginActivity = activityRule.getActivity(); + } + + @Test + public void testTrue() { + assertEquals(true, true); + } + + @Test + public void testRedirectToMainActivity() { + Instrumentation.ActivityMonitor am = InstrumentationRegistry.getInstrumentation().addMonitor(MainActivity.class.getName(), null, true); + facebookLoginActivity.redirectToMainActivity(); + assertEquals(1, am.getHits()); + } +} \ No newline at end of file diff --git a/src/resources/RedundantPrint/src/test/java/org/hwyl/sexytopo/control/util/Space3DTransformerTest.java b/src/resources/RedundantPrint/src/test/java/org/hwyl/sexytopo/control/util/Space3DTransformerTest.java new file mode 100644 index 0000000..b582b46 --- /dev/null +++ b/src/resources/RedundantPrint/src/test/java/org/hwyl/sexytopo/control/util/Space3DTransformerTest.java @@ -0,0 +1,91 @@ +package org.hwyl.sexytopo.control.util; + +import org.hwyl.sexytopo.model.graph.Coord3D; +import org.hwyl.sexytopo.model.survey.Leg; +import org.junit.Assert; +import org.junit.Test; + + +public class Space3DTransformerTest { + + + Space3DTransformer transformer = new Space3DTransformer(); + + @Test + public void testTransform1MNorth() { + Leg north1MLeg = new Leg(1, 0, 0); + Coord3D result = transformer.transform(Coord3D.ORIGIN, north1MLeg); + Coord3D expected = new Coord3D(0, 1, 0); + assertEquals(expected, result); + } + + @Test + public void testTransform1MEast() { + Leg east1MLeg = new Leg(1, 90, 0); + Coord3D result = transformer.transform(Coord3D.ORIGIN, east1MLeg); + Coord3D expected = new Coord3D(1, 0, 0); + assertEquals(expected, result); + } + + @Test + public void testTransform1MUp() { + Leg up1MLeg = new Leg(1, 0, 90); + Coord3D result = transformer.transform(Coord3D.ORIGIN, up1MLeg); + Coord3D expected = new Coord3D(0, 0, 1); + assertEquals(expected, result); + } + + @Test + public void testTransform1MDown() { + Leg up1MLeg = new Leg(1, 0, -90); + Coord3D result = transformer.transform(Coord3D.ORIGIN, up1MLeg); + Coord3D expected = new Coord3D(0, 0, -1); + assertEquals(expected, result); + } + + @Test + public void testTransform2MNorth() { + Leg north2MLeg = new Leg(2, 0, 0); + Coord3D result = transformer.transform(Coord3D.ORIGIN, north2MLeg); + Coord3D expected = new Coord3D(0, 2, 0); + assertEquals(expected, result); + } + + @Test + public void testTransform3N3E3S3W() { + Leg n3 = new Leg(3, 0, 0); + Coord3D result = transformer.transform(Coord3D.ORIGIN, n3); + Leg e3 = new Leg(3, 90, 0); + result = transformer.transform(result, e3); + Leg s3 = new Leg(3, 180, 0); + result = transformer.transform(result, s3); + Leg w3 = new Leg(3, 270, 0); + result = transformer.transform(result, w3); + assertEquals(Coord3D.ORIGIN, result); + } + + @Test + public void testTransform10mNEUAndBack() { + Leg northEastAndUp10M = new Leg(10, 45, 45); + Coord3D result = transformer.transform(Coord3D.ORIGIN, northEastAndUp10M); + System.out.println("result = " + result); + Leg reverse = new Leg(10, 225, -45); + result = transformer.transform(result, reverse); + assertEquals(Coord3D.ORIGIN, result); + } + + private void assertEquals(Coord3D zero, Coord3D one) { + + double x0 = zero.getX(); + double y0 = zero.getY(); + double z0 = zero.getZ(); + double x1 = one.getX(); + double y1 = one.getY(); + double z1 = one.getZ(); + + double delta = 0.0001; + Assert.assertEquals(x0, x1, delta); + Assert.assertEquals(y0, y1, delta); + Assert.assertEquals(z0, z1, delta); + } +} \ No newline at end of file diff --git a/src/resources/ResourceOptimism/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LoggingTest.java b/src/resources/ResourceOptimism/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LoggingTest.java new file mode 100644 index 0000000..92315b6 --- /dev/null +++ b/src/resources/ResourceOptimism/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LoggingTest.java @@ -0,0 +1,110 @@ +package nodomain.freeyourgadget.gadgetbridge.test; + +import android.support.annotation.NonNull; + +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import java.io.File; +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.BuildConfig; +import nodomain.freeyourgadget.gadgetbridge.Logging; +import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * Tests dynamic enablement and disablement of file appenders. + * Test is currently disabled because logback-android does not work + * inside a plain junit test. + */ +@RunWith(RobolectricTestRunner.class) +@Config(constants = BuildConfig.class, sdk = 19) +// need sdk 19 because "WITHOUT ROWID" is not supported in robolectric/sqlite4java +public class LoggingTest { + + private static File logFilesDir; + + public LoggingTest() throws Exception { + } + + @BeforeClass + public static void setupSuite() throws Exception { + // properties might be preconfigured in build.gradle because of test ordering problems + String logDir = System.getProperty(Logging.PROP_LOGFILES_DIR); + if (logDir != null) { + logFilesDir = new File(logDir); + } else { + logFilesDir = FileUtils.createTempDir("logfiles"); + System.setProperty(Logging.PROP_LOGFILES_DIR, logFilesDir.getAbsolutePath()); + } + + if (System.getProperty("logback.configurationFile") == null) { + File workingDir = new File(System.getProperty("user.dir")); + File configFile = new File(workingDir, "src/main/assets/logback.xml"); + System.out.println(configFile.getAbsolutePath()); + System.setProperty("logback.configurationFile", configFile.getAbsolutePath()); + } + } + + private Logging logging = new Logging() { + @Override + protected String createLogDirectory() throws IOException { + return logFilesDir.getAbsolutePath(); + } + }; + + @After + public void tearDown() { + assertTrue(FileUtils.deleteRecursively(getLogFilesDir())); + } + + @NonNull + private File getLogFilesDir() { + String dirName = System.getProperty(Logging.PROP_LOGFILES_DIR); + if (dirName != null && dirName.length() > 5) { + File dir = new File(dirName); + return dir; + } + fail("Property " + Logging.PROP_LOGFILES_DIR + " has invalid value: " + dirName); + return null; // not reached + } + + @Test + public void testToggleLogging() { + try { + File dir = getLogFilesDir(); + } catch (AssertionError ignored) { + // expected, as not yet set up + } + + try { + logging.setupLogging(true); + File dir = getLogFilesDir(); + assertEquals(1, dir.list().length); + assertNotNull(logging.getFileLogger()); + assertTrue(logging.getFileLogger().isStarted()); + + logging.setupLogging(false); + assertNotNull(logging.getFileLogger()); + assertFalse(logging.getFileLogger().isStarted()); + + logging.setupLogging(true); + assertNotNull(logging.getFileLogger()); + assertTrue(logging.getFileLogger().isStarted()); + } catch (AssertionError ex) { + logging.debugLoggingConfiguration(); + System.err.println(System.getProperty("java.class.path")); + throw ex; + } + } +} diff --git a/src/resources/SensitiveEquality/src/test/java/org/ethereum/util/RLPTest.java b/src/resources/SensitiveEquality/src/test/java/org/ethereum/util/RLPTest.java new file mode 100644 index 0000000..52d9a91 --- /dev/null +++ b/src/resources/SensitiveEquality/src/test/java/org/ethereum/util/RLPTest.java @@ -0,0 +1,1136 @@ +package org.ethereum.util; + +import org.ethereum.crypto.HashUtil; + +import com.cedarsoftware.util.DeepEquals; + +import org.ethereum.db.ByteArrayWrapper; +import org.ethereum.net.client.Capability; +import org.ethereum.net.p2p.HelloMessage; +import org.ethereum.net.swarm.Util; +import org.junit.Ignore; +import org.junit.Test; + +import org.spongycastle.util.encoders.Hex; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +import java.math.BigInteger; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import java.util.*; + +import static org.ethereum.util.ByteUtil.byteArrayToInt; +import static org.ethereum.util.ByteUtil.wrap; +import static org.ethereum.util.RLP.*; +import static org.junit.Assert.*; +import static org.ethereum.util.RlpTestData.*; + +public class RLPTest { + + @Test + public void test1() throws UnknownHostException { + + String peersPacket = "F8 4E 11 F8 4B C5 36 81 " + + "CC 0A 29 82 76 5F B8 40 D8 D6 0C 25 80 FA 79 5C " + + "FC 03 13 EF DE BA 86 9D 21 94 E7 9E 7C B2 B5 22 " + + "F7 82 FF A0 39 2C BB AB 8D 1B AC 30 12 08 B1 37 " + + "E0 DE 49 98 33 4F 3B CF 73 FA 11 7E F2 13 F8 74 " + + "17 08 9F EA F8 4C 21 B0"; + + byte[] payload = Hex.decode(peersPacket); + + byte[] ip = decodeIP4Bytes(payload, 5); + + assertEquals(InetAddress.getByAddress(ip).toString(), ("/54.204.10.41")); + } + + @Test + public void test2() throws UnknownHostException { + + String peersPacket = "F8 4E 11 F8 4B C5 36 81 " + + "CC 0A 29 82 76 5F B8 40 D8 D6 0C 25 80 FA 79 5C " + + "FC 03 13 EF DE BA 86 9D 21 94 E7 9E 7C B2 B5 22 " + + "F7 82 FF A0 39 2C BB AB 8D 1B AC 30 12 08 B1 37 " + + "E0 DE 49 98 33 4F 3B CF 73 FA 11 7E F2 13 F8 74 " + + "17 08 9F EA F8 4C 21 B0"; + + byte[] payload = Hex.decode(peersPacket); + int oneInt = decodeInt(payload, 11); + + assertEquals(oneInt, 30303); + } + + @Test + public void test3() throws UnknownHostException { + + String peersPacket = "F8 9A 11 F8 4B C5 36 81 " + + "CC 0A 29 82 76 5F B8 40 D8 D6 0C 25 80 FA 79 5C " + + "FC 03 13 EF DE BA 86 9D 21 94 E7 9E 7C B2 B5 22 " + + "F7 82 FF A0 39 2C BB AB 8D 1B AC 30 12 08 B1 37 " + + "E0 DE 49 98 33 4F 3B CF 73 FA 11 7E F2 13 F8 74 " + + "17 08 9F EA F8 4C 21 B0 F8 4A C4 36 02 0A 29 " + + "82 76 5F B8 40 D8 D6 0C 25 80 FA 79 5C FC 03 13 " + + "EF DE BA 86 9D 21 94 E7 9E 7C B2 B5 22 F7 82 FF " + + "A0 39 2C BB AB 8D 1B AC 30 12 08 B1 37 E0 DE 49 " + + "98 33 4F 3B CF 73 FA 11 7E F2 13 F8 74 17 08 9F " + + "EA F8 4C 21 B0 "; + + byte[] payload = Hex.decode(peersPacket); + + int nextIndex = 5; + byte[] ip = decodeIP4Bytes(payload, nextIndex); + assertEquals("/54.204.10.41", InetAddress.getByAddress(ip).toString()); + + nextIndex = getNextElementIndex(payload, nextIndex); + int port = decodeInt(payload, nextIndex); + assertEquals(30303, port); + + nextIndex = getNextElementIndex(payload, nextIndex); + BigInteger peerId = decodeBigInteger(payload, nextIndex); + + BigInteger expectedPeerId = + new BigInteger("9650128800487972697726795438087510101805200020100629942070155319087371611597658887860952245483247188023303607186148645071838189546969115967896446355306572"); + assertEquals(expectedPeerId, peerId); + + nextIndex = getNextElementIndex(payload, nextIndex); + nextIndex = getFirstListElement(payload, nextIndex); + ip = decodeIP4Bytes(payload, nextIndex); + assertEquals("/54.2.10.41", InetAddress.getByAddress(ip).toString()); + + nextIndex = getNextElementIndex(payload, nextIndex); + port = decodeInt(payload, nextIndex); + assertEquals(30303, port); + + nextIndex = getNextElementIndex(payload, nextIndex); + peerId = decodeBigInteger(payload, nextIndex); + + expectedPeerId = + new BigInteger("9650128800487972697726795438087510101805200020100629942070155319087371611597658887860952245483247188023303607186148645071838189546969115967896446355306572"); + + assertEquals(expectedPeerId, peerId); + + nextIndex = getNextElementIndex(payload, nextIndex); + nextIndex = getFirstListElement(payload, nextIndex); + assertEquals(-1, nextIndex); + } + + @Test + /** encode byte */ + public void test4() { + + byte[] expected = {(byte) 0x80}; + byte[] data = encodeByte((byte) 0); + assertArrayEquals(expected, data); + + byte[] expected2 = {(byte) 0x78}; + data = encodeByte((byte) 120); + assertArrayEquals(expected2, data); + + byte[] expected3 = {(byte) 0x7F}; + data = encodeByte((byte) 127); + assertArrayEquals(expected3, data); + } + + @Test + /** encode short */ + public void test5() { + + byte[] expected = {(byte) 0x80}; + byte[] data = encodeShort((byte) 0); + assertArrayEquals(expected, data); + + byte[] expected2 = {(byte) 0x78}; + data = encodeShort((byte) 120); + assertArrayEquals(expected2, data); + + byte[] expected3 = { (byte) 0x7F}; + data = encodeShort((byte) 127); + assertArrayEquals(expected3, data); + + byte[] expected4 = {(byte) 0x82, (byte) 0x76, (byte) 0x5F}; + data = encodeShort((short) 30303); + assertArrayEquals(expected4, data); + + byte[] expected5 = {(byte) 0x82, (byte) 0x4E, (byte) 0xEA}; + data = encodeShort((short) 20202); + assertArrayEquals(expected5, data); + + byte[] expected6 = {(byte) 0x82, (byte) 0x9D, (byte) 0x0A}; + data = encodeShort((short) 40202); + assertArrayEquals(expected6, data); + } + + @Test + /** encode int */ + public void testEncodeInt() { + + byte[] expected = {(byte) 0x80}; + byte[] data = encodeInt(0); + assertArrayEquals(expected, data); + assertEquals(0, RLP.decodeInt(data, 0)); + + byte[] expected2 = {(byte) 0x78}; + data = encodeInt(120); + assertArrayEquals(expected2, data); + assertEquals(120, RLP.decodeInt(data, 0)); + + byte[] expected3 = {(byte) 0x7F}; + data = encodeInt(127); + assertArrayEquals(expected3, data); + assertEquals(127, RLP.decodeInt(data, 0)); + + assertEquals(256, RLP.decodeInt(RLP.encodeInt(256), 0)); + assertEquals(255, RLP.decodeInt(RLP.encodeInt(255), 0)); + assertEquals(127, RLP.decodeInt(RLP.encodeInt(127), 0)); + assertEquals(128, RLP.decodeInt(RLP.encodeInt(128), 0)); + + data = RLP.encodeInt(1024); + assertEquals(1024, RLP.decodeInt(data, 0)); + + byte[] expected4 = {(byte) 0x82, (byte) 0x76, (byte) 0x5F}; + data = encodeInt(30303); + assertArrayEquals(expected4, data); + assertEquals(30303, RLP.decodeInt(data, 0)); + + byte[] expected5 = {(byte) 0x82, (byte) 0x4E, (byte) 0xEA}; + data = encodeInt(20202); + assertArrayEquals(expected5, data); + assertEquals(20202, RLP.decodeInt(data, 0)); + + byte[] expected6 = {(byte) 0x83, 1, 0, 0}; + data = encodeInt(65536); + assertArrayEquals(expected6, data); + assertEquals(65536, RLP.decodeInt(data, 0)); + + byte[] expected7 = {(byte) 0x84, (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00}; + data = encodeInt(Integer.MIN_VALUE); + assertArrayEquals(expected7, data); + assertEquals(Integer.MIN_VALUE, RLP.decodeInt(data, 0)); + + byte[] expected8 = {(byte) 0x84, (byte) 0x7F, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; + data = encodeInt(Integer.MAX_VALUE); + assertArrayEquals(expected8, data); + assertEquals(Integer.MAX_VALUE, RLP.decodeInt(data, 0)); + + byte[] expected9 = {(byte) 0x84, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; + data = encodeInt(0xFFFFFFFF); + assertArrayEquals(expected9, data); + assertEquals(0xFFFFFFFF, RLP.decodeInt(data, 0)); + + } + + @Test + /** encode BigInteger */ + public void test6() { + + byte[] expected = new byte[]{(byte) 0x80}; + byte[] data = encodeBigInteger(BigInteger.ZERO); + assertArrayEquals(expected, data); + } + + @Test + /** encode string */ + public void test7() { + + byte[] data = encodeString(""); + assertArrayEquals(new byte[]{(byte) 0x80}, data); + + byte[] expected = {(byte) 0x90, (byte) 0x45, (byte) 0x74, (byte) 0x68, (byte) 0x65, (byte) 0x72, (byte) 0x65, + (byte) 0x75, (byte) 0x6D, (byte) 0x4A, (byte) 0x20, (byte) 0x43, (byte) 0x6C, + (byte) 0x69, (byte) 0x65, (byte) 0x6E, (byte) 0x74}; + + String test = "EthereumJ Client"; + data = encodeString(test); + + assertArrayEquals(expected, data); + + String test2 = "Ethereum(++)/ZeroGox/v0.5.0/ncurses/Linux/g++"; + + byte[] expected2 = {(byte) 0xAD, (byte) 0x45, (byte) 0x74, (byte) 0x68, (byte) 0x65, (byte) 0x72, (byte) 0x65, + (byte) 0x75, (byte) 0x6D, (byte) 0x28, (byte) 0x2B, (byte) 0x2B, (byte) 0x29, (byte) 0x2F, + (byte) 0x5A, (byte) 0x65, (byte) 0x72, (byte) 0x6F, (byte) 0x47, (byte) 0x6F, (byte) 0x78, + (byte) 0x2F, (byte) 0x76, (byte) 0x30, (byte) 0x2E, (byte) 0x35, (byte) 0x2E, (byte) 0x30, + (byte) 0x2F, (byte) 0x6E, (byte) 0x63, (byte) 0x75, (byte) 0x72, (byte) 0x73, (byte) 0x65, + (byte) 0x73, (byte) 0x2F, (byte) 0x4C, (byte) 0x69, (byte) 0x6E, (byte) 0x75, (byte) 0x78, + (byte) 0x2F, (byte) 0x67, (byte) 0x2B, (byte) 0x2B}; + + data = encodeString(test2); + assertArrayEquals(expected2, data); + + String test3 = "Ethereum(++)/ZeroGox/v0.5.0/ncurses/Linux/g++Ethereum(++)/ZeroGox/v0.5.0/ncurses/Linux/g++"; + + byte[] expected3 = {(byte) 0xB8, (byte) 0x5A, + (byte) 0x45, (byte) 0x74, (byte) 0x68, (byte) 0x65, (byte) 0x72, (byte) 0x65, + (byte) 0x75, (byte) 0x6D, (byte) 0x28, (byte) 0x2B, (byte) 0x2B, (byte) 0x29, (byte) 0x2F, + (byte) 0x5A, (byte) 0x65, (byte) 0x72, (byte) 0x6F, (byte) 0x47, (byte) 0x6F, (byte) 0x78, + (byte) 0x2F, (byte) 0x76, (byte) 0x30, (byte) 0x2E, (byte) 0x35, (byte) 0x2E, (byte) 0x30, + (byte) 0x2F, (byte) 0x6E, (byte) 0x63, (byte) 0x75, (byte) 0x72, (byte) 0x73, (byte) 0x65, + (byte) 0x73, (byte) 0x2F, (byte) 0x4C, (byte) 0x69, (byte) 0x6E, (byte) 0x75, (byte) 0x78, + (byte) 0x2F, (byte) 0x67, (byte) 0x2B, (byte) 0x2B, + + (byte) 0x45, (byte) 0x74, (byte) 0x68, (byte) 0x65, (byte) 0x72, (byte) 0x65, + (byte) 0x75, (byte) 0x6D, (byte) 0x28, (byte) 0x2B, (byte) 0x2B, (byte) 0x29, (byte) 0x2F, + (byte) 0x5A, (byte) 0x65, (byte) 0x72, (byte) 0x6F, (byte) 0x47, (byte) 0x6F, (byte) 0x78, + (byte) 0x2F, (byte) 0x76, (byte) 0x30, (byte) 0x2E, (byte) 0x35, (byte) 0x2E, (byte) 0x30, + (byte) 0x2F, (byte) 0x6E, (byte) 0x63, (byte) 0x75, (byte) 0x72, (byte) 0x73, (byte) 0x65, + (byte) 0x73, (byte) 0x2F, (byte) 0x4C, (byte) 0x69, (byte) 0x6E, (byte) 0x75, (byte) 0x78, + (byte) 0x2F, (byte) 0x67, (byte) 0x2B, (byte) 0x2B}; + + data = encodeString(test3); + assertArrayEquals(expected3, data); + } + + @Test + /** encode byte array */ + public void test8() { + + String byteArr = "ce73660a06626c1b3fda7b18ef7ba3ce17b6bf604f9541d3c6c654b7ae88b239" + + "407f659c78f419025d785727ed017b6add21952d7e12007373e321dbc31824ba"; + + byte[] byteArray = Hex.decode(byteArr); + + String expected = "b840" + byteArr; + + assertEquals(expected, Hex.toHexString(encodeElement(byteArray))); + } + + @Test + /** encode list */ + public void test9() { + + byte[] actuals = encodeList(); + assertArrayEquals(new byte[]{(byte) 0xc0}, actuals); + } + + @Test + /** encode null value */ + public void testEncodeElementNull() { + + byte[] actuals = encodeElement(null); + assertArrayEquals(new byte[]{(byte) 0x80}, actuals); + } + + @Test + /** encode single byte 0x00 */ + public void testEncodeElementZero() { + + byte[] actuals = encodeElement(new byte[]{0x00}); + assertArrayEquals(new byte[]{0x00}, actuals); + } + + @Test + /** encode single byte 0x01 */ + public void testEncodeElementOne() { + + byte[] actuals = encodeElement(new byte[]{0x01}); + assertArrayEquals(new byte[]{(byte) 0x01}, actuals); + } + + @Test + /** found bug encode list affects element value, + hhh... not really at the end but keep the test */ + public void test10() { + + /* 2 */ + byte[] prevHash = + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + prevHash = encodeElement(prevHash); + + /* 2 */ + byte[] uncleList = HashUtil.sha3(encodeList(new byte[]{})); + + /* 3 */ + byte[] coinbase = + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; + coinbase = encodeElement(coinbase); + + byte[] header = encodeList( + prevHash, uncleList, coinbase); + + assertEquals("f856a000000000000000000000000000000000000000000000000000000000000000001dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000", + Hex.toHexString(header)); + } + + @Test + public void test11() { +// 2240089100000070 + String tx = "F86E12F86B80881BC16D674EC8000094CD2A3D9F938E13CD947EC05ABC7FE734DF8DD8268609184E72A00064801BA0C52C114D4F5A3BA904A9B3036E5E118FE0DBB987FE3955DA20F2CD8F6C21AB9CA06BA4C2874299A55AD947DBC98A25EE895AABF6B625C26C435E84BFD70EDF2F69"; + byte[] payload = Hex.decode(tx); + + Queue index = new LinkedList<>(); + fullTraverse(payload, 0, 0, payload.length, 1, index); + + // TODO: assert lenght overflow while parsing list in RLP + } + + @Test + public void test12() { + + String tx = "F86E12F86B80881BC16D674EC8000094CD2A3D9F938E13CD947EC05ABC7FE734DF8DD8268609184E72A00064801BA0C52C114D4F5A3BA904A9B3036E5E118FE0DBB987FE3955DA20F2CD8F6C21AB9CA06BA4C2874299A55AD947DBC98A25EE895AABF6B625C26C435E84BFD70EDF2F69"; + byte[] payload = Hex.decode(tx); + + RLPList rlpList = decode2(payload); + + RLPList.recursivePrint(rlpList); + // TODO: add some asserts in place of just printing the rlpList + } + + @Test /* very long peers msg */ + public void test13() { + + String peers = "f9 14 90 11 f8 4c c6 81 83 68 81 fc 04 82 76 5f b8 40 07 7e 53 7a 8b 36 73 e8 f1 b6 25 db cc " + + "90 2e a7 d4 ce d9 40 2e 46 64 e8 73 67 95 12 cc 23 60 69 8e 53 42 56 52 a0 46 24 fc f7 8c db a1 a3 " + + "23 30 87 a9 19 a3 4d 11 ae da ce ee b7 d8 33 fc bf 26 f8 4c c6 63 81 e7 58 81 af 82 76 5f b8 40 0a " + + "b2 cd e8 3a 09 84 03 dd c2 ea 54 14 74 0d 8a 01 93 e4 49 c9 6e 11 24 19 96 7a bc 62 eb 17 cd ce d7 " + + "7a e0 ab 07 5e 04 f7 dd dc d4 3f b9 04 8b e5 32 06 a0 40 62 0b de 26 cb 74 3f a3 12 31 9f f8 4d c7 " + + "81 cf 81 db 45 81 9a 82 76 5f b8 40 19 c3 3d a7 03 1c ff 17 7e fa 84 2f aa 3d 31 bd 83 e1 76 4e c6 " + + "10 f2 36 94 4a 9f 8a 21 c1 c5 1a 04 f4 7f 6b 5f c3 ef e6 5c af 36 94 43 63 5a fc 58 d8 f5 d4 e2 f1 " + + "2a f9 ee ec 3c 6e 30 bf 0a 2b f8 4c c6 44 30 81 ad 81 a3 82 76 5f b8 40 1e 59 c2 82 08 12 94 80 84 " + + "97 ae 7a 7e 97 67 98 c4 2b 8b cc e1 3c 9d 8b 0e cf 8a fe cd b5 df d4 ef a8 77 0f c0 d1 f7 de 63 c9 " + + "16 40 e7 e8 b4 35 8c 9e 3e d0 f3 d6 c9 86 20 ad 7e a4 24 18 c9 ec f8 4b c5 1f 12 81 9e 48 82 76 5f " + + "b8 40 1f 68 c0 75 c1 d8 7b c0 47 65 43 0f df b1 e5 d0 0f 1b 78 4e d6 be 72 1e 4c af f7 be b5 7b 4b " + + "21 7b 95 da 19 b5 ec 66 04 58 68 b3 9a ac 2e 08 76 cf 80 f0 b6 8d 0f a2 0b db 90 36 be aa 70 61 ea " + + "f8 4c c6 81 bf 81 ea 39 37 82 76 5f b8 40 21 78 0c 55 b4 7d b4 b1 14 67 b5 f5 5b 0b 55 5e 08 87 ce " + + "36 fb d9 75 e2 24 b1 c7 0e ac 7a b8 e8 c2 db 37 f0 a4 8b 90 ff dd 5a 37 9a da 99 b6 a0 f6 42 9c 4a " + + "53 c2 55 58 19 1a 68 26 36 ae f4 f2 f8 4c c6 44 30 81 ad 81 a3 82 76 5f b8 40 23 15 cb 7c f4 9b 8e " + + "ab 21 2c 5a 45 79 0b 50 79 77 39 73 8f 5f 73 34 39 b1 90 11 97 37 ee 8c 09 bc 72 37 94 71 2a a8 2f " + + "26 70 bc 58 1a b0 75 7e f2 31 37 ac 0f df 0f 8c 89 65 e7 dd 6b a7 9f 8c f8 4e c8 81 bf 81 b1 81 d1 " + + "81 9f 82 76 5f b8 40 24 9a 36 41 e5 a8 d0 8e 41 a5 cf c8 da e1 1f 17 61 25 4f 4f d4 7d 9b 13 33 8d " + + "b8 e6 e3 72 9e 6f 2a c9 ec 09 7a 5c 80 96 84 d6 2a 41 e6 df c2 ff f7 2d c3 db d9 7e a2 61 32 bb 97 " + + "64 05 65 bb 0c f8 4a c4 55 41 7e 2d 82 76 5f b8 40 2a 38 ea 5d 9a 7e fd 7f ff c0 a8 1d 8e a7 ed 28 " + + "31 1c 40 12 bb ab 14 07 c8 da d2 68 51 29 e0 42 17 27 34 a3 28 e8 90 7f 90 54 b8 22 5f e7 70 41 d8 " + + "a4 86 a9 79 76 d2 83 72 42 ab 6c 8c 59 05 e4 f8 4c c6 81 83 68 81 fc 04 82 76 5f b8 40 32 4d d9 36 " + + "38 4d 8c 0d de fd e0 4b a7 40 29 98 ab bd 63 d7 9c 0b f8 58 6b 3d d2 c7 db f6 c9 1e b8 0a 7b 6d e8 " + + "f1 6a 50 04 4f 14 9c 7b 39 aa fb 9c 3a d7 f2 ca a4 03 55 aa b0 98 88 18 6f cc a2 f8 4c c6 44 30 81 " + + "ad 81 a3 82 76 5f b8 40 39 42 45 c0 99 16 33 ed 06 0b af b9 64 68 53 d3 44 18 8b 80 4f e3 7e 25 a5 " + + "bc ac 44 ed 44 3a 84 a6 8b 3a af 15 5e fe 48 61 e8 4b 4b 51 5f 9a 5d ec db d7 da e9 81 92 d7 a3 20 " + + "a7 92 c7 d4 df af f8 4d c7 56 81 b7 81 e7 81 cd 82 76 5f b8 40 39 86 50 f6 7b 22 92 93 9d e3 4c 0e " + + "ae b9 14 1f 94 84 a0 fb 17 3f a3 3f 81 a1 f7 31 5d 0e b7 7b de 3a 76 c3 86 36 fa e6 6f a1 4b f2 af " + + "df d6 3e 60 ab d4 0e 29 b0 2a 91 4e 65 de 57 89 98 3f d4 f8 4c c6 44 81 b9 81 ea 40 82 76 5f b8 40 " + + "3a 15 58 7a 1c 3a da bf 02 91 b3 07 f7 1b 2c 04 d1 98 aa e3 6b 83 49 95 d3 30 5d ff 42 f1 ab 86 f4 " + + "83 ae 12 9e 92 03 fb c6 ef 21 87 c8 62 1e dd 18 f6 1d 53 ea a5 b5 87 ff de a4 d9 26 48 90 38 f8 4d " + + "c7 81 cf 81 db 45 81 9a 82 76 5f b8 40 3b 14 62 04 0e a7 78 e3 f7 5e 65 ce 24 53 41 8a 66 2e 62 12 " + + "c9 f6 5b 02 ea b5 8d 22 b2 87 e4 50 53 bd e5 eb f0 60 96 0c bf a0 d9 dc 85 bf 51 ba 7a a1 f2 ca a2 " + + "c1 36 82 d9 32 77 64 1d 60 db eb f8 4c c6 6a 81 a8 0e 81 f9 82 76 5f b8 40 3e cc 97 ab 15 d2 2f 7b " + + "9e df 19 c0 4c e3 b6 09 5f a2 50 42 14 00 2b 35 98 9c 6f 81 ee 4b 96 1c c2 a8 99 c4 94 15 c9 14 e3 " + + "13 90 83 40 04 7d 1d 3b 25 d7 4f 5b 9c 85 a0 6a fa 26 59 a5 39 99 2e f8 4b c5 2e 04 81 c1 09 82 76 " + + "5f b8 40 40 7c 22 00 3f 3b ba a6 cb eb 8e 4b 0a b7 07 30 73 fe ab 85 18 2b 40 55 25 f8 bd 28 32 55 " + + "04 3d 71 35 18 f7 47 48 d9 2c 43 fb b9 9e cc 7c 3f ba b9 5d 59 80 06 51 3a a8 e5 9c 48 04 1c 8b 41 " + + "c2 f8 4b c5 32 7e 56 81 c2 82 76 5f b8 40 40 8c 93 24 20 3b d8 26 2f ce 65 06 ba 59 dc dd 56 70 89 " + + "b0 eb 9a 5b b1 83 47 7b ab bf 61 63 91 4a cd c7 f4 95 f8 96 4d 8a c1 2f e2 40 18 87 b8 cd 8d 97 c0 " + + "c9 dc cf ad db b2 0a 3c 31 47 a7 89 f8 4a c4 26 6c 4f 68 82 76 5f b8 40 42 3e 40 04 da 2f a7 50 0b " + + "c0 12 c0 67 4a a6 57 15 02 c5 3a a4 d9 1e fa 6e 2b 5c b1 e4 68 c4 62 ca 31 14 a2 e2 eb 09 65 b7 04 " + + "4f 9c 95 75 96 5b 47 e4 7a 41 f1 3f 1a dc 03 a2 a4 b3 42 d7 12 8d f8 4b c5 40 81 e7 08 2d 82 76 5f " + + "b8 40 42 83 93 75 27 2c 2f 3d ea db 28 08 5d 06 05 5e 35 31 35 c6 c8 d8 96 09 7a 1b c4 80 c4 88 4f " + + "d1 60 45 18 cb df 73 1a c1 8f 09 84 b7 f0 21 48 e8 82 90 d1 3c 22 4d 82 46 43 14 e2 b5 96 2e 3f 89 " + + "f8 4d c7 32 81 aa 81 d8 81 c8 82 76 5f b8 40 44 cf 19 44 6c a4 65 01 8e 4d e6 c6 0f c0 df 52 9e ba " + + "25 02 92 ef 74 41 e1 db 59 84 1c 69 f0 22 f6 09 28 10 c9 a5 a7 f2 74 f2 f9 7c 4b d6 c7 6e ad c0 64 " + + "c7 d6 59 7c ae b1 7e d8 7c b2 57 73 5f f8 4b c5 32 81 9c 5a 53 82 76 5f b8 40 46 1c 9b 54 e9 19 53 " + + "c5 bb c3 1c 67 12 a9 17 38 2b e6 7d 60 f7 5e b7 f5 06 51 be a3 e5 94 d0 d1 9c 22 29 d8 f6 6a db 3f " + + "20 3f 60 00 38 e7 cc 93 4d c9 27 87 fa c4 39 2b 9b fa 7c bc 78 6f d0 5b f8 4b c5 81 86 64 7d 29 82 " + + "76 5f b8 40 48 35 3a 00 58 e2 64 48 d9 4e 59 33 6c ca 9d 28 a9 37 41 20 de f7 6c 4b cc fe e1 8b 01 " + + "23 e5 91 92 39 3a 2e e3 04 4d 80 e0 ee cb b0 94 76 be 62 fd e1 e8 74 f9 3d 05 ea 5c 4a 9a 45 c0 6e " + + "8f e1 f8 4b c5 4e 08 05 81 bb 82 76 5f b8 40 48 e8 95 09 49 d4 c0 0b cd bb e9 39 c5 bf 07 8f 2c bf " + + "f1 08 84 af 16 60 b1 c3 22 b9 ca a3 ba 35 7b b4 15 7f c6 b0 03 9a f9 43 8d fe 51 ec 27 8a 47 fc d3 " + + "b7 26 fa 0a 08 7d 4c 3c 01 a6 2f 33 5e f8 4a c6 58 45 81 c6 81 c6 07 b8 40 4a 02 55 fa 46 73 fa a3 " + + "0f c5 ab fd 3c 55 0b fd bc 0d 3c 97 3d 35 f7 26 46 3a f8 1c 54 a0 32 81 cf ff 22 c5 f5 96 5b 38 ac " + + "63 01 52 98 77 57 a3 17 82 47 85 49 c3 6f 7c 84 cb 44 36 ba 79 d6 d9 f8 4b c5 40 81 e7 08 2d 82 76 " + + "5f b8 40 4c 75 47 ab 4d 54 1e 10 16 4c d3 74 1f 34 76 ed 19 4b 0a b9 a1 36 df ca c3 94 3f 97 35 8c " + + "9b 05 14 14 27 36 ca 2f 17 0f 12 52 29 05 7b 47 32 44 a6 23 0b f5 47 1a d1 68 18 85 24 b2 b5 cd 8b " + + "7b f8 4c c6 44 30 81 ad 81 a3 82 76 5f b8 40 4d 5e 48 75 d6 0e b4 ee af b6 b2 a7 d3 93 6e d3 c9 bc " + + "58 ac aa de 6a 7f 3c 5f 25 59 8c 20 b3 64 f1 2b ea 2f b1 db 3b 2c 2e f6 47 85 a4 7d 6b 6b 5b 10 34 " + + "27 cb ac 0c 88 b1 8f e9 2a 9f 53 93 f8 f8 4b c5 52 0c 81 e3 54 82 76 5f b8 40 4f d8 98 62 75 74 d3 " + + "e8 6b 3f 5a 65 c3 ed c2 e5 da 84 53 59 26 e4 a2 88 20 b0 03 8b 19 63 6e 07 db 5e b0 04 d7 91 f8 04 " + + "1a 00 6e 33 e1 08 e4 ec 53 54 99 d1 28 d8 d9 c5 ca f6 bb dc 22 04 f7 6a f8 4b c5 81 b4 20 2b 08 82 " + + "76 5f b8 40 53 cc f2 5a b5 94 09 ec bb 90 3d 2e c3 a9 aa 2e b3 9d 7c c4 c7 db 7e 6f 68 fd 71 1a 7c " + + "eb c6 06 21 6d e7 37 82 6d a4 20 93 e3 e6 52 1e e4 77 0e b2 d6 69 dc 4b f3 54 6c c7 57 c3 40 12 69 " + + "6e ae f8 4c c6 6a 81 a8 0e 81 f9 82 76 5f b8 40 54 b3 93 15 69 91 39 87 80 50 2f a8 f4 14 13 79 bc " + + "e2 69 31 be 87 ba 8e 0b 74 9b a9 05 a9 e9 76 e5 de 6d 39 c9 8c f0 48 f2 5c 3c bb b8 c7 f3 02 c4 e6 " + + "04 ad 5b f7 2c db 06 10 0f 50 0d e3 a6 86 f8 4a c4 4c 67 37 47 82 76 5f b8 40 60 0a 77 fb 14 e7 92 " + + "c0 c7 0d c4 ad e3 82 ed 60 43 62 b9 78 b1 9b 94 c4 ed 18 83 38 a1 79 5d 2d b4 5f 7f 22 3b 66 ba eb " + + "a3 91 c5 9b 55 88 b4 4e ba f7 1c 7e b3 97 55 c2 72 29 c7 fd e6 41 be ce f8 4b c5 6d 2b 81 9a 42 82 " + + "76 5f b8 40 69 dd 44 5f 67 c3 be f3 94 f9 54 9f da e1 62 3d bc 20 88 4a 62 fd 56 16 dd bb 49 f8 4b " + + "a8 7e 14 7c b8 a5 0b a9 71 d7 30 c4 62 1d 0e b6 51 33 49 4e 94 fa 5e a2 e6 9c 66 1f 6b 12 e7 ed 2a " + + "8d 4e f8 4b c5 18 09 3d 81 9b 82 76 5f b8 40 6b 5d 4c 35 ff d1 f5 a1 98 03 8a 90 83 4d 29 a1 b8 8b " + + "e0 d5 ef ca 08 bc 8a 2d 58 81 18 0b 0b 41 6b e0 06 29 aa be 45 0a 50 82 8b 8d 1e e8 2d 98 f5 52 81 " + + "87 ee 67 ed 6e 07 3b ce ef cd fb 2b c9 f8 4a c4 55 41 7e 2d 82 76 5f b8 40 6c bb 1e d5 36 dc 38 58 " + + "c1 f0 63 42 9b d3 95 2a 5d 32 ef 8e 11 52 6c df e7 2f 41 fe a1 ac e9 60 18 7c 99 75 ab bc 23 78 35 " + + "11 c0 0f 26 98 35 47 47 f9 05 aa ac 11 dc d2 b7 47 8b 3e af 32 7a c6 f8 4b c5 40 81 e7 08 2d 82 76 " + + "5f b8 40 6e a2 8f 64 ea 1c c3 b6 57 25 44 fd 5b f7 43 b0 ea ab e0 17 f5 14 73 0c 89 7d a3 c7 7f 03 " + + "c5 16 f1 e5 f3 1d 79 3b 4b ce 3c aa 1d ed 56 35 6d 20 b2 eb b5 5a 70 66 f4 1c 25 b7 c3 d5 66 14 e0 " + + "6b f8 4a c4 55 41 7e 2d 82 76 5f b8 40 72 53 24 08 e8 be 6d 5e 2c 9f 65 0f b9 c9 f9 96 50 cc 1f a0 " + + "62 a4 a4 f2 cf e4 e6 ae 69 cd d2 e8 b2 3e d1 4a fe 66 95 5c 23 fa 04 8f 3a 97 6e 3c e8 16 9e 50 5b " + + "6a 89 cc 53 d4 fa c2 0c 2a 11 bf f8 4c c6 52 81 d9 48 81 a9 82 76 5f b8 40 7a ee a4 33 60 b9 36 8b " + + "30 e7 f4 82 86 61 3f d1 e3 b0 20 7f b7 1f 03 08 d5 04 12 11 44 63 e7 7a b8 30 27 c0 d4 0c ad aa b8 " + + "bb f6 12 fc 5b 69 67 fa 1c 40 73 29 d4 7e c6 1f b0 dc 3d a1 08 68 32 f8 4c c6 81 a6 81 93 53 4f 82 " + + "76 5f b8 40 7b 3c dd e0 58 d5 b4 5d 8d b2 24 36 60 cf ea 02 e0 74 ec 21 31 14 c2 51 d7 c0 c3 2d 04 " + + "03 bb 7a b4 77 13 d2 49 2f f6 c8 81 cf c2 aa c3 f5 2c b2 69 76 8c 89 68 f3 b6 b1 8b ac 97 22 d0 53 " + + "31 f6 f8 4c c6 6a 81 a8 0e 81 f9 82 76 5f b8 40 87 ab 58 1b b9 7c 21 2a 2d a7 ef 0d 6e 10 5e 41 b5 " + + "5e 4e 42 cb b6 a1 af 9a 76 1a 01 ca 8c 65 06 9a b4 b5 82 7e 32 2c f2 c5 f5 9e 7f 59 2b e2 a8 17 c4 " + + "5a b6 41 f5 a9 dd 36 89 63 c7 3f 9e e6 88 f8 4c c6 52 81 d9 48 81 a9 82 76 5f b8 40 8c 66 0d bc 6d " + + "3d b0 18 6a d1 0f 05 fd 4f 2f 06 43 77 8e c5 14 e8 45 2a 75 50 c6 30 da 21 17 1a 29 b1 bb 67 c2 e8 " + + "e1 01 ea 1d b3 97 43 f3 e7 8c 4d 26 76 a1 3d 15 51 51 21 51 5f c3 8b 04 8f 37 f8 4c c6 63 81 e7 58 " + + "81 af 82 76 5f b8 40 94 fe 3d 52 a2 89 4c ed c6 b1 54 24 15 6e b8 73 8a 84 41 dd 74 ba 9c ed 66 64 " + + "ed 30 a3 32 a9 5b 57 4d 89 26 2e a3 67 fa 90 0a e9 70 6f b8 1a 40 82 87 bd de f3 a9 dd 9f f4 4e 3a " + + "41 bc 09 0f dc f8 4d c7 81 d5 81 81 81 e6 0a 82 76 5f b8 40 95 21 14 f1 10 e8 ac 00 df ea 5f 05 0d " + + "95 5e 76 4c 7c ba 8f b2 07 c0 5a 7a a5 ae 84 91 68 64 0a 2b 4e 31 43 91 fc 3a 76 79 5b 38 27 05 54 " + + "62 63 9c ff 4a e2 d6 4a b8 0e 95 27 44 28 31 3e 36 6a f8 4c c6 58 45 81 c6 81 c6 82 76 5f b8 40 96 " + + "f3 47 b0 96 ed 16 30 f4 74 b9 76 23 e4 5e 8d 47 1b 1d 43 c2 2f 59 96 07 c8 b2 e3 ed 0d 7b 79 05 d8 " + + "55 4a d3 99 db d7 39 c7 61 26 40 44 24 d8 db 0d c7 d2 b0 47 c1 a3 28 ae 27 d4 09 06 c5 83 f8 4c c6 " + + "81 83 68 81 fc 04 82 76 5f b8 40 9a 22 c8 fb 1b d8 bb d0 2f 0e 74 ed 9d 3d 55 b0 f5 b0 96 72 bc 43 " + + "a2 d4 7b 1e d0 42 38 c1 c3 2b 6a 65 74 26 52 5b 15 51 82 36 e9 78 9b 54 6a 4a 07 2a 60 5e 13 73 fe " + + "5b 99 6b ae dc 30 35 94 28 f8 4b c5 52 0c 81 e3 54 82 76 5f b8 40 9b 1a 3a 8d 77 1b 3d 94 9c a3 94 " + + "a8 8e b5 dc 29 a9 53 b0 2c 81 f0 17 36 1f fc 0a fe 09 ab ce 30 69 17 1a 87 d4 74 52 36 87 fc c9 a9 " + + "d3 2c c0 2c fa b4 13 22 56 fe aa bf e0 5f 7a c7 47 19 4e 88 f8 4b c5 42 81 d7 78 1c 82 76 5f b8 40 " + + "9f a7 e5 5b 2d 98 f1 d7 44 c7 62 32 e4 fd a2 42 fe 9f d3 d5 74 3d 16 d3 ca d2 e5 48 a0 7c b5 af 06 " + + "fe 60 eb ae b8 c6 09 50 28 17 92 34 dc dd d3 cd cf 1f cf e6 ed aa 2a 53 30 7f d1 03 da 4a f0 f8 4a " + + "c4 55 41 7e 2d 82 76 5f b8 40 a0 1f 83 4e 9d 1a 61 3c 3c 74 7e 56 1c ac 19 cb 12 d8 79 c1 a5 74 20 " + + "a4 9c 23 65 2b 8f 51 28 8c 8b 11 1a a3 88 89 98 b0 5e 32 7f 47 a2 35 c6 a4 a3 77 f8 88 e3 00 5a 2d " + + "4b 03 ec b7 26 86 08 d3 f8 4c c6 44 30 81 ad 81 a3 82 7a 51 b8 40 a5 fd 77 c0 d4 32 fb fa 33 17 08 " + + "49 14 c2 e8 a8 82 1e 4b a1 dc ba 44 96 1f f7 48 0e 6d b6 08 78 9c ab 62 91 41 63 60 ea 8c dc 26 b0 " + + "d2 f0 87 7c 50 e8 9a 70 c1 bc f5 d6 dd 8b 18 2e 0a 9e 37 d3 f8 4d c7 81 88 81 a0 81 98 31 82 76 5f " + + "b8 40 ae 31 bd 02 54 ee 7d 10 b8 0f c9 0e 74 ba 06 ba 76 11 87 df 31 38 a9 79 9d e5 82 8d 01 63 52 " + + "4c 44 ba c7 d2 a9 b5 c4 1b e5 be 82 89 a1 72 36 1f 0b a9 04 10 c9 4f 57 9b f7 eb d2 8f 18 aa a1 cd " + + "f8 4a c4 55 41 7e 2d 82 76 5f b8 40 ba 3d 21 67 72 cd c7 45 58 d2 54 56 24 a2 d6 2d cb cf d2 72 30 " + + "57 30 c7 46 43 c7 a7 e8 19 af a6 cd d8 22 23 e2 b5 50 1e b6 d4 ea e5 db f2 1e 55 8c 76 8a ca ec 2c " + + "1c a1 0e 74 c4 c8 7a 57 4b 53 f8 4a c4 55 41 7e 2d 82 76 5f b8 40 bd b4 9c 01 87 2d 91 bd 1e a9 90 " + + "bd 2e df 16 c4 81 71 a6 06 7f 9a 6f 7f 48 bf b1 94 63 0b 5a e9 03 1b 5d c2 63 f5 9c 66 ad a4 44 cb " + + "4e 6f 9d f6 2b 30 17 ce 61 2c ab 7b 53 da 08 d3 56 f7 8d 30 f8 4c c6 63 81 e7 58 81 af 82 76 5f b8 " + + "40 c1 2b a9 1f 95 04 4d 78 ee d1 d3 a9 53 5e bd 64 71 52 44 18 13 5e eb 46 ad 5d 5c 6e cc 2f 51 68 " + + "b4 ab 3a 06 2b b0 74 2a ea 65 ff ea 76 7f ab 8d cc 21 78 3c b2 9b f3 2e 2c d6 22 22 09 fa 71 fd f8 " + + "4c c6 44 30 81 ad 81 a3 82 7a 51 b8 40 c2 e2 69 e6 4a a8 c9 be 2d 41 81 2a 48 af a2 34 6b d4 1a 1a " + + "b2 e4 64 62 41 ae 3b 8d 0c cd 41 f2 d6 82 b1 5a 02 5f 75 9c 0d 95 5a 60 71 d4 e8 ea 7d 4d e3 97 d6 " + + "e0 52 23 09 20 11 3b 6e b7 4c 09 f8 4a c4 4a 4f 17 77 82 76 5f b8 40 c3 03 b8 3f 6a 16 1f 99 67 36 " + + "34 44 80 ae 9d 88 fd c1 d9 c6 75 bf ac a8 88 f7 0f 24 89 72 65 62 82 09 da 53 74 1e 03 c0 f6 59 21 " + + "f6 8f 60 2d c9 f3 34 a3 c4 5b cb 92 af 85 44 a6 fb 11 9b d8 87 f8 4b c5 0c 81 fa 61 1a 82 76 5f b8 " + + "40 c7 6e 7c 15 7b 77 35 51 11 53 d1 f9 50 81 a1 44 e0 88 a9 89 17 1f 3d 43 2c c5 d8 29 3e ce 9c fa " + + "a4 83 c0 32 15 5d 7b 53 65 6a 6e 33 a3 d7 5c d0 62 4e 09 a2 f9 49 c1 56 09 3d ba a8 3f 11 11 f2 f8 " + + "4b c5 52 0c 81 e3 54 82 76 5f b8 40 c7 d5 a3 69 1a 59 59 9d e3 33 48 9c bf 8a 47 a7 43 3e 92 c7 27 " + + "06 e1 3d 94 ed 21 12 96 d3 5c 97 d8 35 7d 7e 07 b3 85 85 64 d7 26 8e d7 aa 09 7f 37 58 9c 27 77 0f " + + "90 dd 0b 07 63 5b e3 f5 33 64 f8 4c c6 4e 09 81 92 81 b2 82 76 5f b8 40 c8 81 97 a8 2b 0a cf 0a 87 " + + "24 94 d1 df ac 9d e8 46 da a7 de 08 b2 40 64 7a 96 ba 72 fb e0 8f d5 2b 55 c6 c9 45 14 a4 7e c5 1b " + + "a4 9a 97 54 89 eb c9 38 3b 48 f5 e2 40 93 90 68 ce 58 36 ff 24 f1 f8 4b c5 81 b4 20 2b 08 82 76 5f " + + "b8 40 c9 e0 39 d8 a8 b9 e4 35 be f2 f4 5f c7 cb 7e 78 87 16 e8 c7 af c1 ba cc 64 e1 24 6d 2a b5 06 " + + "d3 60 73 79 2a e6 96 e4 1a d6 ba 0c 8a bd 2e c0 d5 45 b0 75 7f 94 a9 f3 53 82 80 e5 6d b5 f5 d8 ec " + + "f8 4b c5 4e 68 81 a3 51 82 76 5f b8 40 ca 27 68 37 02 a8 e9 bf 32 01 65 6f f8 4a 60 d5 b1 dd 81 42 " + + "73 99 3c f1 a0 25 b0 54 45 4e 40 d5 30 92 f4 85 18 ee 05 be ad 4f 18 02 1f 4f 54 0c 0b 7c 7d 26 eb " + + "a5 0e a4 89 0b 9e 5e 49 a7 6c 5f f8 4a c4 55 41 7e 2d 82 76 5f b8 40 cb 72 be 9e 2e 5d 4a 1f 25 72 " + + "96 c7 39 39 10 4e ce 80 31 32 15 26 5a f0 6b c7 ea f4 42 ab ff 4f 0b 48 fc fc 6f 43 f4 df 46 30 c7 " + + "12 b5 e7 ef db 75 4a 86 e4 0c f2 02 16 6e b6 9e ea a6 ad 3a 2d f8 4a c4 36 48 1f 37 82 76 5f b8 40 " + + "ce 73 66 0a 06 62 6c 1b 3f da 7b 18 ef 7b a3 ce 17 b6 bf 60 4f 95 41 d3 c6 c6 54 b7 ae 88 b2 39 40 " + + "7f 65 9c 78 f4 19 02 5d 78 57 27 ed 01 7b 6a dd 21 95 2d 7e 12 00 73 73 e3 21 db c3 18 24 ba f8 4a " + + "c4 55 41 7e 2d 82 76 5f b8 40 ce 73 f1 f1 f1 f1 6c 1b 3f da 7b 18 ef 7b a3 ce 17 b6 f1 f1 f1 f1 41 " + + "d3 c6 c6 54 b7 ae 88 b2 39 40 7f f1 f1 f1 f1 19 02 5d 78 57 27 ed 01 7b 6a dd 21 f1 f1 f1 f1 00 00 " + + "01 e3 21 db c3 18 24 ba f8 4c c6 81 bf 81 ea 39 37 82 76 5f b8 40 d2 30 30 60 35 99 b7 6f 64 0b 8f " + + "7c 11 99 12 bb 04 66 e7 ee f3 38 cd 9d e5 67 d2 b6 df ba 81 72 8d b2 e9 8f 29 38 25 bb 00 a9 a6 ac " + + "93 66 83 fc 82 c8 bc 38 7a df 3a 4a 5f e1 cc ca dd 1a 74 59 f8 4c c6 6b 81 aa 39 81 f7 82 76 5f b8 " + + "40 e0 2b 18 fb a6 b8 87 fb 92 58 46 9c 3a f8 e4 45 cc 9a e2 b5 38 6c ac 5f 60 c4 17 0f 82 20 86 22 " + + "4e 38 76 55 5c 74 5a 7e c8 ac 18 1c 7f 97 01 77 6d 94 a7 79 60 4e a1 26 51 de 5f 4a 74 8d 29 e1 f8 " + + "4c c6 40 81 e7 0a 81 d0 82 76 5f b8 40 e3 11 15 a7 6f a7 fb 2e fd 3c fa f4 6a d0 0b 05 fc 34 98 e1 " + + "ba f1 78 5d ff e6 ca 69 91 3d 25 65 31 d1 80 56 42 35 fd 3d 3c 10 40 9c d1 1f c2 59 cf 7c fd a9 b6 " + + "bb 25 33 40 41 2d 82 87 8f 3b d3 f8 4b c5 41 5e 31 81 97 82 76 5f b8 40 e5 e8 d8 c2 d7 62 d2 1c a1 " + + "e9 bc ee 8a dc 53 60 0f 2d 89 40 97 54 26 66 d6 b5 f4 1b 23 58 4b 07 f6 09 01 ab 40 9d df 91 e0 cd " + + "25 62 da ff f2 cb 0f 22 1e b9 f1 15 6f 78 1a 5d 99 31 a0 2a 2e 07 f8 4a c4 55 41 7e 2d 82 76 5f b8 " + + "40 ea 99 2c 13 68 7c 20 e7 90 a9 ff a6 df 8b 1a 16 86 88 e2 a8 87 36 5d 7a 50 21 86 fa 0d 62 20 e8 " + + "3e 11 3a 1f e7 7d c0 68 9d 55 ba 2e 8a 83 aa 8e 20 42 18 f4 d8 e7 32 82 5b d7 80 cf 94 ed 5c c3 f8 " + + "4b c5 56 7c 52 81 fe 82 76 5f b8 40 f6 15 5f 1a 60 14 3b 7d 9d 5d 1a 44 0d 7d 52 fe 68 09 f6 9e 0c " + + "6f 1e 00 24 45 7e 0d 71 dd 88 ad e3 b1 3a aa 94 0c 89 ac 06 10 95 2b 48 bd 83 2c 42 e3 43 a1 3e 61 " + + "ff db 06 01 0c ff c3 45 e0 53 f8 4c c6 63 81 e7 58 81 af 82 76 5f b8 40 fa 56 85 61 b7 d5 28 8d f7 " + + "a5 06 c9 bc 1c 95 12 ab 39 6e 68 c4 6f 0e 62 c2 1d c1 aa 58 4b 84 4a 8a 7e 94 4f 69 71 30 36 65 fd " + + "37 b1 38 d9 a5 f6 37 e6 72 ed b9 89 69 66 4c 4e 7f d1 c4 12 6d ef"; + byte[] payload = Hex.decode(peers); + + RLPList rlpList = decode2(payload); + + RLPList.recursivePrint(rlpList); + // TODO: add some asserts in place of just printing the rlpList + } + + @Test /* very very very long blocks msg */ + public void test14() { + + String blocksMsg = "f91c1c13f90150f8c4a07df3d35d4df0a56fcf1d6344d5315cb56b9bf83bb96ad17c7b96a9cd14133c5da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a064afb6284fa35f26d7b2c5a26afaa5483072fbcb575221b34ce002a991b7a223a04a8abe6d802797dc80b497584f898c2d4fd561cc185828cfa1b92f6f38ee348e833fbfe484533f201c80a000000000000000000000000000000000000000000000000000cfccb5cfd4667cf887f8850380942d0aceee7e5ab874e22ccf8d1a649f59106d74e88609184e72a000822710a047617600000000000000000000000000000000000000000000000000000000001ca08691ab40e258de3c4f55c868c0c34e780e747158a1d96ca50186dfd3305abd78a042269c981d048a7b791aafc8f4e644232740c1a1cceb5b6d05568827a64c0664c0f8c8f8c4a0637c8a6cdb907fac6f752334ab79065bcc4e46cd4f4358dbc2a653544a20eb31a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a022a36c1a1e807e6afc22e6bb53a31111f56e7ee7dbb2ee571cefb152b514db4da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fcfd784533f1cf980a0000000000000000000000000000000000000000000000000e153d743fa040b18c0c0f8c8f8c4a07b2536237cbf114a043b0f9b27c76f84ac160ea5b87b53e42c7e76148964d450a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a07a3be0ee10ece4b03097bf74aabac628aa0fae617377d30ab1b97376ee31f41aa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fbfe884533f1ce880a0000000000000000000000000000000000000000000000000f3deea84969b6e95c0c0f8c8f8c4a0d2ae3f5dd931926de428d99611980e7cdd7c1b838273e43fcad1b94da987cfb8a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a00b5b11fcf4ee12c6812f9d01cf0dff07c72cd7e02e48b35682f67c595407be14a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833faffd84533f1ce280a00000000000000000000000000000000000000000000000005fcbc97b2eb8ffb3c0c0f8c8f8c4a094d615d3cb4b306d20985028c78f0c8413d509a75e8bb84eda95f734debad2a0a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a04b8fd1b98cf5758bd303ff86393eb6d944c1058124bddce5d4e04b5395254d5ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fbfec84533f1c7680a000000000000000000000000000000000000000000000000079fe3710116b32aac0c0f8c8f8c4a09424a07a0e4c05bb872906c40844a75b76f6517467b79c12fa9cc6d79ae09934a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a02dbe9ff9cbbc4c5a6ff26971f75b405891141f4e9bce3c2dc4200a305138e584a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fcfdf84533f1c3b80a0000000000000000000000000000000000000000000000000e0a6f8cf1d56031bc0c0f8c8f8c4a009aabea60cf7eaa9df4afdf4e1b5f3e684dab34fc9a9180a050085a4131ceedfa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a0436da067f9683029e717edf92da46c3443e8c342974f47a563302a0678efe702a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fdfd684533f1bfc80a00000000000000000000000000000000000000000000000005bc88c041662ffdac0c0f8c8f8c4a0f8b104093483b7c0182e1bba2ce3340d14469d3a3ee7646821223a676c680ac1a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a0d482e71cde61190a33ca5aeb88b6b06276984e5a14253a98df232e8767167221a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fefd184533f1bce80a00000000000000000000000000000000000000000000000004aeb31823f6a1950c0c0f8c8f8c4a0dd1f0aba02c2bb3b5a2b6cb1cc907ea70912bd46dc7a78577f2cae6cdbcbe5f3a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a058ab6df33d7cbeb6a735a7e4ccf4f28143e6a1742e45dda8f8cf48af43cb66c0a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fffd084533f1b9f80a0000000000000000000000000000000000000000000000000577042b0858b510bc0c0f8c8f8c4a0a287bb7da30f04344976abe569bd719f69c1cbea65533e5311ca5862e6eaa504a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a07e0537009c23cb1152caf84a52272431f74b6140866b15805622b7bcb607cd42a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934783400fd384533f1b6180a000000000000000000000000000000000000000000000000083d31378a0993e1ac0c0f8c8f8c4a063483cff8fbd489e6ce273326d8dc1d54a31c67f936ca84bf500e5419d3e9805a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a07737d08564819d51f8f834a6ee4278c23a0c2f29a3f485b21002c1f24f04d8e4a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fffd484533f1b5780a0000000000000000000000000000000000000000000000000bb586fe6de016e14c0c0f8c8f8c4a0975c8ed0c9197b7c018e02e1c95f315acf82b25e4a812140f4515e8bc827650ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a0ad51229abead59e93c80db5ba160f0939bc49dcee65d1c081f6eb040ff1f571fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fefd984533f1b4e80a0000000000000000000000000000000000000000000000000548f02c6ceb26fd4c0c0f8c8f8c4a05844082e41f7c1f34485c7902afa0aa0979a6a849100fc553cd946f4a663929ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a01bc726443437c4c062be18d052278e4ef735b8fe84387c8a4fc85fb70d5078e0a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fffd884533f1b1080a0000000000000000000000000000000000000000000000000cc1e528f54f22bdac0c0f8c8f8c4a0ba06ba81c93faaf98ea2d83cbdc0788958d938b29a9eb2a92ffbd4a628b3d52ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a05053bfe1c0f1f0dd341c6df35e5a659989be041e8521027cc90f7605eb15fbb9a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fefdd84533f1b0380a0000000000000000000000000000000000000000000000000bcf9df2fec615ecac0c0f8c8f8c4a083732d997db15109e90464c24b7c959a78881d827c55a0d668a66a2736be5d87a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a054f4012cba33a2b80b0dca9dd52f56b2c588133bd71700863f8cb95127176634a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fffdc84533f1a4680a00000000000000000000000000000000000000000000000006920a1dc9d915d0ec0c0f8c8f8c4a052e2fba761c2d0643170ef041c017391e781190fe715ae87cdae8eee1d45d95ba01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a0ee2c82f77d7afd1f8dbe4f791df8477496c23e5504b9d66814172077f65f81f2a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fefe184533f1a3880a0000000000000000000000000000000000000000000000000ae86da9012398fc4c0c0f8c8f8c4a055703ba09544f386966b6d63bfc31033b761a4d1a6bb86b0cf49b4bb0526744ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a01684c03a675b53786f0077d1651c3d169a009b13a6ee2b5047be6dbbe6d957ffa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fdfea84533f1a2f80a00000000000000000000000000000000000000000000000003247320d0eb639dfc0c0f8c8f8c4a05109a79b33d81f4ee4814b550fb0002f03368d67570f6d4e6105fce2874d8b72a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a0ae72e8c60a3dcfd53deecdb2790d18f0cc710f77cf2c1ed76e7da829bde619dca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fcff784533f1a1d80a000000000000000000000000000000000000000000000000040e0bc9bc9bcf295c0c0f8c8f8c4a03961e4bbba5c95fad3db0cffa3a16b9106f9ea3e8957993eab576b683c22f416a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a0e9c6cf457bbe64d6bda67852a276cdbadb4f384a36d496e81801a496cfd9b7b5a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fdfee84533f19df80a0000000000000000000000000000000000000000000000000dbb3fd6c816776d8c0c0f8c8f8c4a06b8265a357cb3ad744e19f04eb15377f660c10c900cc352d24f9b09073a363d6a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a07ba07e1bc6a20ffa44ae6080d30982b9faa148faf6b1ec15e32d89ac853ac291a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fefe984533f198d80a00000000000000000000000000000000000000000000000005171325b6a2f17f1c0c0f8c8f8c4a0dcdc0347bb87ce72d49ac2e4e11f89069509b129a2536bf3d57c6bca30894032a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a0ca24447aa0cedb4b561c7810461eef19b16a827c27352e5e01f914e9d7c78247a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fffe884533f194680a0000000000000000000000000000000000000000000000000da4714cfed9d8bbcc0c0f8c8f8c4a047f2dd6c15ea4082b3e11e5cf6b925b27e51d9de68051a093e52ef465cffbb8ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a05a7206edddf50fcfeeaa97348a7112fc6edd0b5eacb44cf43d6a6c6b6609b459a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fefed84533f193e80a0000000000000000000000000000000000000000000000000ffafba4bf8dc944ec0c0f8c8f8c4a04d5ad6d860772145872f6660ecefcb0b0b2056e0aa3509a48bf4c175459e5121a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a00f4659d09bb2ced56e7fd9c4d3d90daca8b4f471307b7c4385fd34a41016b0b2a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fdff684533f192580a000000000000000000000000000000000000000000000000090620e5e59a39fe5c0c0f8c8f8c4a0c1725c58d1bf023af468e0088db3cf642ae097cf2c58c2ece2fc746980acc7e6a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a0be19a182ea1584050deb0a79abdc11be896ce8d00a282bcfaf9ffcd65fd64d6aa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833feff184533f189080a000000000000000000000000000000000000000000000000076f17f4199bccd12c0c0f8c8f8c4a0bd521a20a18ca6ca7115065065a1aa20067ee580fb11e2963d1e3a681e8302afa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a011be45633350e39475a1a07712ba72de4602d9eebf639ccd5422a389095ccaf1a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fdffa84533f187b80a00000000000000000000000000000000000000000000000000c71b81c4a4cb82cc0c0f8c8f8c4a07c6d2d56e9c87f1553e4d06705af61a7c19a6046d2c39f8ed1417988783d3b1da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a012f5f0668063509e33a45a64eb6a072b2d84aa19f430f49f159be5008a786b2ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fd00684533f186080a0000000000000000000000000000000000000000000000000b3f962892cfec9e6c0c0f8c8f8c4a07154f0f8ecc7f791d22eec06ec86d87a44b2704f015b3d2cff3571a3d01ae0f6a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a079536abf8e163cf8aa97f0d52866d04363902d591fd7c36aa35fc983d45fefd6a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fdffd84533f182f80a0000000000000000000000000000000000000000000000000736716e42499890fc0c0f8c8f8c4a0bf2fb1ee988ac4e17eae221a24176649774333fab25b6fc96c6081527fb6f121a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a041578daae7bcccd4976340aeb19e4132d2fe4193a0d92f87744c82bfe113502fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fd00984533f182b80a00000000000000000000000000000000000000000000000001c62fa76645942c6c0c0f8c8f8c4a07f84873e2679d40458b9dda9900478a78871044e08f6b47dad659b9b60ff8d48a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a0597d3f4160770c0492333f90bad739dc05117d0e478a91f09573742e432904e8a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fe00184533f17f680a0000000000000000000000000000000000000000000000000e24d8b1140fb34d5c0c0f8c8f8c4a0fd77bd13a8cde1766537febe751a27a2a31310a04638a1afcd5e8ad3c5485453a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a0473b2b248d91010ba9aec2696ffc93c11c415ed132832be0fd0578f184862e13a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833feffc84533f17ca80a0000000000000000000000000000000000000000000000000fb5b65bac3f0d947c0c0f8c8f8c4a0518916dfb79c390bd7bff75712174512c2f96bec42a3f573355507ad1588ce0ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a08599d2ec9e95ec62f41a4975b655d8445d6767035f94eb235ed5ebea976fb9eaa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347833fe00484533f17b880a0000000000000000000000000000000000000000000000000bc27f4b8a201476bc0c0f90319f8c4a0ab6b9a5613970faa771b12d449b2e9bb925ab7a369f0a4b86b286e9d540099cfa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943854aaf203ba5f8d49b1ec221329c7aebcf050d3a0990dc3b5acbee04124361d958fe51acb582593613fc290683940a0769549d3eda09bfe4817d274ea3eb8672e9fe848c3885b53bbbd1d7c26e6039f90fb96b942b0833ff00084533f16b780a000000000000000000000000000000000000000000000000077377adff6c227dbf9024ff89d80809400000000000000000000000000000000000000008609184e72a000822710b3606956330c0d630000003359366000530a0d630000003359602060005301356000533557604060005301600054630000000c5884336069571ca07f6eb94576346488c6253197bde6a7e59ddc36f2773672c849402aa9c402c3c4a06d254e662bf7450dd8d835160cbb053463fed0b53f2cdd7f3ea8731919c8e8ccf9010501809400000000000000000000000000000000000000008609184e72a000822710b85336630000002e59606956330c0d63000000155933ff33560d63000000275960003356576000335700630000005358600035560d630000003a590033560d63000000485960003356573360003557600035335700b84a7f4e616d65526567000000000000000000000000000000000000000000000000003057307f4e616d655265670000000000000000000000000000000000000000000000000057336069571ba04af15a0ec494aeac5b243c8a2690833faa74c0f73db1f439d521c49c381513e9a05802e64939be5a1f9d4d614038fbd5479538c48795614ef9c551477ecbdb49d2f8a6028094ccdeac59d35627b7de09332e819d5159e7bb72508609184e72a000822710b84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d0aceee7e5ab874e22ccf8d1a649f59106d74e81ba0d05887574456c6de8f7a0d172342c2cbdd4cf7afe15d9dbb8b75b748ba6791c9a01e87172a861f6c37b5a9e3a5d0d7393152a7fbe41530e5bb8ac8f35433e5931bc0"; + byte[] payload = Hex.decode(blocksMsg); + + RLPList rlpList = decode2(payload); + + RLPList.recursivePrint(rlpList); + // TODO: add some asserts in place of just printing the rlpList + } + + @Test /* hello msg */ + public void test15() { + + String helloMsg = "f8 91 80 0b 80 b8 46 45 74 68 65 72 65 75 6d 28 2b 2b 29 2f 5a 65 72 6f 47 6f 78 2e 70 72 " + + "69 63 6b 6c 79 5f 6d 6f 72 73 65 2f 76 30 2e 34 2e 32 2f 52 65 6c 65 61 73 65 2d 57 69 6e 33 32 2f " + + "57 69 6e 64 6f 77 73 2f 56 53 32 30 31 33 07 82 76 5f b8 40 ea 99 2c 13 68 7c 20 e7 90 a9 ff a6 df " + + "8b 1a 16 86 88 e2 a8 87 36 5d 7a 50 21 86 fa 0d 62 20 e8 3e 11 3a 1f e7 7d c0 68 9d 55 ba 2e 8a 83 " + + "aa 8e 20 42 18 f4 d8 e7 32 82 5b d7 80 cf 94 ed 5c c3"; + byte[] payload = Hex.decode(helloMsg); + + RLPList rlpList = decode2(payload); + + RLPList.recursivePrint(rlpList); + // TODO: add some asserts in place of just printing the rlpList + } + + /************************************ + * Test data from: https://github.com/ethereum/wiki/wiki/%5BEnglish%5D-RLP + * + * Using assertEquals(String, String) instead of assertArrayEquals to see the actual content when the test fails. + */ + @Test(expected = RuntimeException.class) + public void testEncodeNull() { + encode(null); + } + + @Test + public void testEncodeEmptyString() { + String test = ""; + String expected = "80"; + byte[] encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + String decodeResult = (String) decode(encoderesult, 0).getDecoded(); + assertEquals(test, decodeResult); + } + + @Test + public void testEncodeShortString() { + String test = "dog"; + String expected = "83646f67"; + byte[] encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + byte[] decodeResult = (byte[]) decode(encoderesult, 0).getDecoded(); + assertEquals(test, bytesToAscii(decodeResult)); + } + + @Test + public void testEncodeSingleCharacter() { + String test = "d"; + String expected = "64"; + byte[] encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + byte[] decodeResult = (byte[]) decode(encoderesult, 0).getDecoded(); + assertEquals(test, bytesToAscii(decodeResult)); + } + + @Test + public void testEncodeLongString() { + String test = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"; // length = 56 + String expected = "b8384c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e7365637465747572206164697069736963696e6720656c6974"; + byte[] encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + byte[] decodeResult = (byte[]) decode(encoderesult, 0).getDecoded(); + assertEquals(test, bytesToAscii(decodeResult)); + } + + @Test + public void testEncodeZero() { + Integer test = 0; + String expected = "80"; + byte[] encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + String decodeResult = (String) decode(encoderesult, 0).getDecoded(); + assertEquals("", decodeResult); + } + + @Test + public void testEncodeSmallInteger() { + Integer test = 15; + String expected = "0f"; + byte[] encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + byte[] decodeResult = (byte[]) decode(encoderesult, 0).getDecoded(); + int result = byteArrayToInt(decodeResult); + assertEquals(test, Integer.valueOf(result)); + } + + @Test + public void testEncodeMediumInteger() { + Integer test = 1000; + String expected = "8203e8"; + byte[] encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + byte[] decodeResult = (byte[]) decode(encoderesult, 0).getDecoded(); + int result = byteArrayToInt(decodeResult); + assertEquals(test, Integer.valueOf(result)); + + test = 1024; + expected = "820400"; + encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + decodeResult = (byte[]) decode(encoderesult, 0).getDecoded(); + result = byteArrayToInt(decodeResult); + assertEquals(test, Integer.valueOf(result)); + } + + @Test + public void testEncodeBigInteger() { + BigInteger test = new BigInteger("100102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 16); + String expected = "a0100102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"; + byte[] encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + byte[] decodeResult = (byte[]) decode(encoderesult, 0).getDecoded(); + assertEquals(test, new BigInteger(1, decodeResult)); + } + + @Test + public void TestEncodeEmptyList() { + Object[] test = new Object[0]; + String expected = "c0"; + byte[] encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + Object[] decodeResult = (Object[]) decode(encoderesult, 0).getDecoded(); + assertTrue(decodeResult.length == 0); + } + + @Test + public void testEncodeShortStringList() { + String[] test = new String[]{"cat", "dog"}; + String expected = "c88363617483646f67"; + byte[] encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + Object[] decodeResult = (Object[]) decode(encoderesult, 0).getDecoded(); + assertEquals("cat", bytesToAscii((byte[]) decodeResult[0])); + assertEquals("dog", bytesToAscii((byte[]) decodeResult[1])); + + test = new String[]{"dog", "god", "cat"}; + expected = "cc83646f6783676f6483636174"; + encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + decodeResult = (Object[]) decode(encoderesult, 0).getDecoded(); + assertEquals("dog", bytesToAscii((byte[]) decodeResult[0])); + assertEquals("god", bytesToAscii((byte[]) decodeResult[1])); + assertEquals("cat", bytesToAscii((byte[]) decodeResult[2])); + } + + @Test + public void testEncodeLongStringList() { + String element1 = "cat"; + String element2 = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"; + String[] test = new String[]{element1, element2}; + String expected = "f83e83636174b8384c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e7365637465747572206164697069736963696e6720656c6974"; + byte[] encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + Object[] decodeResult = (Object[]) decode(encoderesult, 0).getDecoded(); + assertEquals(element1, bytesToAscii((byte[]) decodeResult[0])); + assertEquals(element2, bytesToAscii((byte[]) decodeResult[1])); + } + + //multilist: + //in: [ 1, ["cat"], "dog", [ 2 ] ], + //out: "cc01c48363617483646f67c102" + //in: [ [ ["cat"], ["dog"] ], [ [1] [2] ], [] ], + //out: "cdc88363617483646f67c20102c0" + @Test + public void testEncodeMultiList() { + Object[] test = new Object[]{1, new Object[]{"cat"}, "dog", new Object[]{2}}; + String expected = "cc01c48363617483646f67c102"; + byte[] encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + Object[] decodeResult = (Object[]) decode(encoderesult, 0).getDecoded(); + assertEquals(1, byteArrayToInt((byte[]) decodeResult[0])); + assertEquals("cat", bytesToAscii(((byte[]) ((Object[]) decodeResult[1])[0]))); + assertEquals("dog", bytesToAscii((byte[]) decodeResult[2])); + assertEquals(2, byteArrayToInt(((byte[]) ((Object[]) decodeResult[3])[0]))); + + test = new Object[]{new Object[]{"cat", "dog"}, new Object[]{1, 2}, new Object[]{}}; + expected = "cdc88363617483646f67c20102c0"; + encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + decodeResult = (Object[]) decode(encoderesult, 0).getDecoded(); + assertEquals("cat", bytesToAscii(((byte[]) ((Object[]) decodeResult[0])[0]))); + assertEquals("dog", bytesToAscii(((byte[]) ((Object[]) decodeResult[0])[1]))); + assertEquals(1, byteArrayToInt(((byte[]) ((Object[]) decodeResult[1])[0]))); + assertEquals(2, byteArrayToInt(((byte[]) ((Object[]) decodeResult[1])[1]))); + assertTrue((((Object[]) decodeResult[2]).length == 0)); + } + + @Test + public void testEncodeEmptyListOfList() { + // list = [ [ [], [] ], [] ], + Object[] test = new Object[]{new Object[]{new Object[]{}, new Object[]{}}, new Object[]{}}; + String expected = "c4c2c0c0c0"; + byte[] encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + Object[] decodeResult = (Object[]) decode(encoderesult, 0).getDecoded(); + assertTrue(decodeResult.length == 2); + assertTrue(((Object[]) (decodeResult[0])).length == 2); + assertTrue(((Object[]) (decodeResult[1])).length == 0); + assertTrue(((Object[]) ((Object[]) (decodeResult[0]))[0]).length == 0); + assertTrue(((Object[]) ((Object[]) (decodeResult[0]))[1]).length == 0); + } + + //The set theoretical representation of two + @Test + public void testEncodeRepOfTwoListOfList() { + //list: [ [], [[]], [ [], [[]] ] ] + Object[] test = new Object[]{new Object[]{}, new Object[]{new Object[]{}}, new Object[]{new Object[]{}, new Object[]{new Object[]{}}}}; + String expected = "c7c0c1c0c3c0c1c0"; + byte[] encoderesult = encode(test); + assertEquals(expected, Hex.toHexString(encoderesult)); + + Object[] decodeResult = (Object[]) decode(encoderesult, 0).getDecoded(); + assertTrue(decodeResult.length == 3); + assertTrue(((Object[]) (decodeResult[0])).length == 0); + assertTrue(((Object[]) (decodeResult[1])).length == 1); + assertTrue(((Object[]) (decodeResult[2])).length == 2); + assertTrue(((Object[]) ((Object[]) (decodeResult[1]))[0]).length == 0); + assertTrue(((Object[]) ((Object[]) (decodeResult[2]))[0]).length == 0); + assertTrue(((Object[]) ((Object[]) (decodeResult[2]))[1]).length == 1); + assertTrue(((Object[]) ((Object[]) ((Object[]) (decodeResult[2]))[1])[0]).length == 0); + } + + @Test + public void testRlpEncode() { + + assertEquals(result01, Hex.toHexString(encode(test01))); + assertEquals(result02, Hex.toHexString(encode(test02))); + assertEquals(result03, Hex.toHexString(encode(test03))); + assertEquals(result04, Hex.toHexString(encode(test04))); + assertEquals(result05, Hex.toHexString(encode(test05))); + assertEquals(result06, Hex.toHexString(encode(test06))); + assertEquals(result07, Hex.toHexString(encode(test07))); + assertEquals(result08, Hex.toHexString(encode(test08))); + assertEquals(result09, Hex.toHexString(encode(test09))); + assertEquals(result10, Hex.toHexString(encode(test10))); + assertEquals(result11, Hex.toHexString(encode(test11))); + assertEquals(result12, Hex.toHexString(encode(test12))); + assertEquals(result13, Hex.toHexString(encode(test13))); + assertEquals(result14, Hex.toHexString(encode(test14))); + assertEquals(result15, Hex.toHexString(encode(test15))); + assertEquals(result16, Hex.toHexString(encode(test16))); + } + + @Test + public void testRlpDecode() { + int pos = 0; + String emptyString; + byte[] decodedData; + Object[] decodedList; + + emptyString = (String) decode(Hex.decode(result01), pos).getDecoded(); + assertEquals("", emptyString); + + emptyString = (String) decode(Hex.decode(result02), pos).getDecoded(); + assertEquals(test02, emptyString); + + decodedData = (byte[]) decode(Hex.decode(result03), pos).getDecoded(); + assertEquals(test03, bytesToAscii(decodedData)); + + decodedData = (byte[]) decode(Hex.decode(result04), pos).getDecoded(); + assertEquals(test04, bytesToAscii(decodedData)); + + decodedData = (byte[]) decode(Hex.decode(result05), pos).getDecoded(); + assertEquals(test05, bytesToAscii(decodedData)); + + decodedList = (Object[]) decode(Hex.decode(result06), pos).getDecoded(); + assertEquals(test06[0], bytesToAscii((byte[]) decodedList[0])); + assertEquals(test06[1], bytesToAscii((byte[]) decodedList[1])); + + decodedList = (Object[]) decode(Hex.decode(result07), pos).getDecoded(); + assertEquals(test07[0], bytesToAscii((byte[]) decodedList[0])); + assertEquals(test07[1], bytesToAscii((byte[]) decodedList[1])); + assertEquals(test07[2], bytesToAscii((byte[]) decodedList[2])); + + // 1 + decodedData = (byte[]) decode(Hex.decode(result08), pos).getDecoded(); + assertEquals(test08, byteArrayToInt(decodedData)); + + // 10 + decodedData = (byte[]) decode(Hex.decode(result09), pos).getDecoded(); + assertEquals(test09, byteArrayToInt(decodedData)); + + // 100 + decodedData = (byte[]) decode(Hex.decode(result10), pos).getDecoded(); + assertEquals(test10, byteArrayToInt(decodedData)); + + // 1000 + decodedData = (byte[]) decode(Hex.decode(result11), pos).getDecoded(); + assertEquals(test11, byteArrayToInt(decodedData)); + + decodedData = (byte[]) decode(Hex.decode(result12), pos).getDecoded(); + assertTrue(test12.compareTo(new BigInteger(1, decodedData)) == 0); + + decodedData = (byte[]) decode(Hex.decode(result13), pos).getDecoded(); + assertTrue(test13.compareTo(new BigInteger(1, decodedData)) == 0); + + // Need to test with different expected value, because decoding doesn't recognize types + Object testObject1 = decode(Hex.decode(result14), pos).getDecoded(); + assertTrue(DeepEquals.deepEquals(expected14, testObject1)); + + Object testObject2 = decode(Hex.decode(result15), pos).getDecoded(); + assertTrue(DeepEquals.deepEquals(test15, testObject2)); + + // Need to test with different expected value, because decoding doesn't recognize types + Object testObject3 = decode(Hex.decode(result16), pos).getDecoded(); + assertTrue(DeepEquals.deepEquals(expected16, testObject3)); + } + + @Test + public void testEncodeLength() { + + // length < 56 + int length = 1; + int offset = 128; + byte[] encodedLength = encodeLength(length, offset); + String expected = "81"; + assertEquals(expected, Hex.toHexString(encodedLength)); + + // 56 > length < 2^64 + length = 56; + offset = 192; + encodedLength = encodeLength(length, offset); + expected = "f838"; + assertEquals(expected, Hex.toHexString(encodedLength)); + } + + @Test + @Ignore + public void unsupportedLength() { + + int length = 56; + int offset = 192; + byte[] encodedLength; + + // length > 2^64 + // TODO: Fix this test - when casting double to int, information gets lost since 'int' is max (2^31)-1 + double maxLength = Math.pow(256, 8); + + try { + encodedLength = encodeLength((int) maxLength, offset); + System.out.println("length: " + length + ", offset: " + offset + ", encoded: " + Arrays.toString(encodedLength)); + + fail("Expecting RuntimeException: 'Input too long'"); + } catch (RuntimeException e) { + // Success! + } + + } + + // Code from: http://stackoverflow.com/a/4785776/459349 + private String bytesToAscii(byte[] b) { + String hex = Hex.toHexString(b); + StringBuilder output = new StringBuilder(); + for (int i = 0; i < hex.length(); i += 2) { + String str = hex.substring(i, i + 2); + output.append((char) Integer.parseInt(str, 16)); + } + return output.toString(); + } + + @Test + public void performanceDecode() throws IOException { + boolean performanceEnabled = false; + + if (performanceEnabled) { + String blockRaw = "f8cbf8c7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a02f4399b08efe68945c1cf90ffe85bbe3ce978959da753f9e649f034015b8817da00000000000000000000000000000000000000000000000000000000000000000834000008080830f4240808080a004994f67dc55b09e814ab7ffc8df3686b4afb2bb53e60eae97ef043fe03fb829c0c0"; + byte[] payload = Hex.decode(blockRaw); + + final int ITERATIONS = 10000000; + RLPList list = null; + DecodeResult result = null; + System.out.println("Starting " + ITERATIONS + " decoding iterations..."); + + long start1 = System.currentTimeMillis(); + for (int i = 0; i < ITERATIONS; i++) { + result = decode(payload, 0); + } + long end1 = System.currentTimeMillis(); + + long start2 = System.currentTimeMillis(); + for (int i = 0; i < ITERATIONS; i++) { + list = decode2(payload); + } + long end2 = System.currentTimeMillis(); + + System.out.println("Result RLP.decode()\t: " + (end1 - start1) + "ms and\t " + determineSize(result) + " bytes for each resulting object list"); + System.out.println("Result RLP.decode2()\t: " + (end2 - start2) + "ms and\t " + determineSize(list) + " bytes for each resulting object list"); + } else { + System.out.println("Performance test for RLP.decode() disabled"); + } + } + + private int determineSize(Serializable ser) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(ser); + oos.close(); + return baos.size(); + } + + + @Test // found this with a bug - nice to keep + public void encodeEdgeShortList() { + + String expectedOutput = "f7c0c0b4600160003556601359506301000000600035040f6018590060005660805460016080530160005760003560805760203560003557"; + + byte[] rlpKeysList = Hex.decode("c0"); + byte[] rlpValuesList = Hex.decode("c0"); + byte[] rlpCode = Hex.decode("b4600160003556601359506301000000600035040f6018590060005660805460016080530160005760003560805760203560003557"); + byte[] output = encodeList(rlpKeysList, rlpValuesList, rlpCode); + + assertEquals(expectedOutput, Hex.toHexString(output)); + } + + + @Test + public void encodeBigIntegerEdge_1() { + + BigInteger integer = new BigInteger("80", 10); + byte[] encodedData = encodeBigInteger(integer); + System.out.println(Hex.toHexString(encodedData)); + } + + @Test + public void testEncodeListHeader(){ + + byte[] header = encodeListHeader(10); + String expected_1 = "ca"; + assertEquals(expected_1, Hex.toHexString(header)); + + header = encodeListHeader(1000); + String expected_2 = "f903e8"; + assertEquals(expected_2, Hex.toHexString(header)); + + header = encodeListHeader(1000000000); + String expected_3 = "fb3b9aca00"; + assertEquals(expected_3, Hex.toHexString(header)); + } + + + @Test + public void testEncodeSet_1(){ + + Set data = new HashSet<>(); + + ByteArrayWrapper element1 = + new ByteArrayWrapper(Hex.decode("1111111111111111111111111111111111111111111111111111111111111111")); + + ByteArrayWrapper element2 = + new ByteArrayWrapper(Hex.decode("2222222222222222222222222222222222222222222222222222222222222222")); + + data.add(element1); + data.add(element2); + + byte[] setEncoded = encodeSet(data); + + RLPList list = (RLPList) decode2(setEncoded).get(0); + + byte[] element1_ = list.get(0).getRLPData(); + byte[] element2_ = list.get(1).getRLPData(); + + assertTrue(data.contains(wrap(element1_))); + assertTrue(data.contains(wrap(element2_))); + } + + @Test + public void testEncodeSet_2(){ + + Set data = new HashSet<>(); + byte[] setEncoded = encodeSet(data); + assertEquals("c0", Hex.toHexString(setEncoded)); + } + + @Test + public void testEncodeInt_7f(){ + String result = Hex.toHexString(encodeInt(0x7f)); + String expected = "7f"; + assertEquals(expected, result); + } + + @Test + public void testEncodeInt_80(){ + String result = Hex.toHexString(encodeInt(0x80)); + String expected = "8180"; + assertEquals(expected, result); + } + + + @Test + public void testEncode_ED(){ + String result = Hex.toHexString(encode(0xED)); + String expected = "81ed"; + assertEquals(expected, result); + } + + + @Test // capabilities: (eth:60, bzz:0, shh:2) + public void testEncodeHelloMessageCap0(){ + + List capabilities = new ArrayList<>(); + capabilities.add(new Capability("eth", (byte) 0x60)); + capabilities.add(new Capability("shh", (byte) 0x02)); + capabilities.add(new Capability("bzz", (byte) 0x00)); + + HelloMessage helloMessage = new HelloMessage((byte)4, + "Geth/v0.9.29-4182e20e/windows/go1.4.2", + capabilities , 30303, + "a52205ce10b39be86507e28f6c3dc08ab4c3e8250e062ec47c6b7fa13cf4a4312d68d6c340315ef953ada7e19d69123a1b902ea84ec00aa5386e5d550e6c550e"); + + byte[] rlp = helloMessage.getEncoded(); + + HelloMessage helloMessage_ = new HelloMessage(rlp); + + String eth = helloMessage_.getCapabilities().get(0).getName(); + byte eth_60 = helloMessage_.getCapabilities().get(0).getVersion(); + + assertEquals("eth", eth); + assertEquals(0x60, eth_60); + + String shh = helloMessage_.getCapabilities().get(1).getName(); + byte shh_02 = helloMessage_.getCapabilities().get(1).getVersion(); + + assertEquals("shh", shh); + assertEquals(0x02, shh_02); + + String bzz = helloMessage_.getCapabilities().get(2).getName(); + byte bzz_00 = helloMessage_.getCapabilities().get(2).getVersion(); + + assertEquals("bzz", bzz); + assertEquals(0x00, bzz_00); + } + + @Test + public void partialDataParseTest() { + String hex = "000080c180000000000000000000000042699b1104e93abf0008be55f912c2ff"; + RLPList el = (RLPList) decode2OneItem(Hex.decode(hex), 3); + assertEquals(1, el.size()); + assertEquals(0, Util.rlpDecodeInt(el.get(0))); + } + + @Test + public void shortStringRightBoundTest(){ + String testString = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; //String of length 55 + byte[] rlpEncoded = encode(testString); + String res = new String((byte[])decode(rlpEncoded, 0).getDecoded()); + assertEquals(testString, res); //Fails + } +} \ No newline at end of file diff --git a/src/resources/SleepyTest/src/sk/baka/aedict/ResultActivityTest.java b/src/resources/SleepyTest/src/sk/baka/aedict/ResultActivityTest.java new file mode 100644 index 0000000..e57ef44 --- /dev/null +++ b/src/resources/SleepyTest/src/sk/baka/aedict/ResultActivityTest.java @@ -0,0 +1,301 @@ +/** + * Aedict - an EDICT browser for Android + Copyright (C) 2009 Martin Vysny + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +package sk.baka.aedict; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; + +import sk.baka.aedict.dict.DictEntry; +import sk.baka.aedict.dict.DictTypeEnum; +import sk.baka.aedict.dict.EdictEntry; +import sk.baka.aedict.dict.MatcherEnum; +import sk.baka.aedict.dict.SearchQuery; +import sk.baka.aedict.kanji.RomanizationEnum; +import android.app.Activity; +import android.content.Intent; +import android.widget.ListView; + +/** + * Tests the ResultActivity activity. + * + * @author Martin Vysny + * + */ +public class ResultActivityTest extends AbstractAedictTest { + + public ResultActivityTest() { + super(ResultActivity.class); + } + + public void testSimpleEnglishSearch() throws Exception { + final SearchQuery q = new SearchQuery(DictTypeEnum.Edict); + q.isJapanese = false; + q.matcher = MatcherEnum.Exact; + q.query = new String[] { "mother" }; + final List result = launch(q); + assertTrue(tester.getText(R.id.textSelectedDictionary).contains("Default")); + final DictEntry entry = result.get(0); + assertEquals("(n) (hum) mother/(P)", entry.english); + assertEquals("母", entry.getJapanese()); + assertEquals("はは", entry.reading); + assertEquals(38, result.size()); + } + + private List launch(final SearchQuery q) throws Exception { + final Intent i = new Intent(getInstrumentation().getContext(), ResultActivity.class); + i.putExtra(ResultActivity.INTENTKEY_SEARCH_QUERY, (Serializable) Collections.singletonList(q)); + tester.startActivity(i); + final ListView lv = getActivity().getListView(); + assertEquals(1, lv.getCount()); + final DictEntry entry = (DictEntry) lv.getItemAtPosition(0); + assertEquals("Searching", entry.english); + Thread.sleep(500); + tester.assertRequestedActivity(ResultActivity.class); + final Intent i2 = getStartedActivityIntent(); + final List result = (List) i2.getSerializableExtra(ResultActivity.INTENTKEY_RESULT_LIST); + return result; + } + + private void launch(boolean isSimeji) { + final Intent i = new Intent(getInstrumentation().getContext(), ResultActivity.class); + final DictEntry entry = new EdictEntry("母", "はは", "(n) (hum) mother/(P)"); + i.putExtra(ResultActivity.INTENTKEY_RESULT_LIST, (Serializable) Collections.singletonList(entry)); + if (isSimeji) { + i.putExtra(ResultActivity.INTENTKEY_SIMEJI, isSimeji); + } + final SearchQuery q = new SearchQuery(DictTypeEnum.Edict); + q.isJapanese = false; + q.matcher = MatcherEnum.Exact; + q.query = new String[] { "mother" }; + i.putExtra(ResultActivity.INTENTKEY_SEARCH_QUERY, (Serializable) Collections.singletonList(q)); + tester.startActivity(i); + } + + private void launch() { + launch(false); + } + + public void testSimpleJapaneseSearch() throws Exception { + final SearchQuery q = new SearchQuery(DictTypeEnum.Edict); + q.isJapanese = true; + q.matcher = MatcherEnum.Exact; + q.query = new String[] { RomanizationEnum.Hepburn.toHiragana("haha") }; + final List result = launch(q); + assertTrue(tester.getText(R.id.textSelectedDictionary).contains("Default")); + assertEquals(1, result.size()); + final DictEntry entry = result.get(0); + assertEquals("(n) (hum) mother/(P)", entry.english); + assertEquals("母", entry.getJapanese()); + } + + public void testSubstringJapaneseSearch() throws Exception { + final SearchQuery q = new SearchQuery(DictTypeEnum.Edict); + q.isJapanese = true; + q.matcher = MatcherEnum.Substring; + q.query = new String[] { RomanizationEnum.Hepburn.toHiragana("haha"), RomanizationEnum.Hepburn.toKatakana("haha") }; + final List result = launch(q); + assertTrue(tester.getText(R.id.textSelectedDictionary).contains("Default")); + final DictEntry entry = result.get(0); + assertEquals("(n) (hum) mother/(P)", entry.english); + assertEquals("母", entry.getJapanese()); + assertEquals(36, result.size()); + } + + /** + * Test for the http://code.google.com/p/aedict/issues/detail?id=30 bug. The + * problem was that there are ~2500 matches for kyou however only the first + * 100 were retrieved from Lucene and they were further filtered. + */ + public void testSearchForKyou() throws Exception { + final SearchQuery q = new SearchQuery(DictTypeEnum.Edict); + q.isJapanese = true; + q.matcher = MatcherEnum.Exact; + q.query = new String[] { RomanizationEnum.Hepburn.toHiragana("kyou") }; + final List result = launch(q); + assertTrue(tester.getText(R.id.textSelectedDictionary).contains("Default")); + assertEquals(18, result.size()); + DictEntry entry = result.get(0); + assertEquals("(n) (1) imperial capital (esp. Kyoto)/(2) final word of an iroha-uta/(3) 10^16/10,000,000,000,000,000/ten quadrillion (American)/(obs) ten thousand billion (British)/(P)", entry.english); + assertEquals("京", entry.getJapanese()); + entry = (DictEntry) result.get(6); + assertEquals("(n-t) (1) today/this day/(P)", entry.english); + assertEquals("今日", entry.getJapanese()); + } + + public void testSwitchToRomaji() { + launch(); + tester.optionMenu(10000); + assertTrue(getActivity().showRomaji.isShowingRomaji()); + } + + public void testShowEntryDetail() { + launch(); + final ListView lv = getActivity().getListView(); + lv.performItemClick(null, 0, 0); + tester.assertRequestedActivity(EdictEntryDetailActivity.class); + final DictEntry entry = (DictEntry) getStartedActivityIntent().getSerializableExtra(EdictEntryDetailActivity.INTENTKEY_ENTRY); + assertEquals("(n) (hum) mother/(P)", entry.english); + assertEquals("母", entry.getJapanese()); + assertEquals("はは", entry.reading); + } + + public void testAddToNotepad() { + launch(); + final ListView lv = getActivity().getListView(); + tester.contextMenu(lv, 1, 0); + tester.assertRequestedActivity(NotepadActivity.class); + final DictEntry entry = (DictEntry) getStartedActivityIntent().getSerializableExtra(NotepadActivity.INTENTKEY_ADD_ENTRY); + assertEquals("(n) (hum) mother/(P)", entry.english); + assertEquals("母", entry.getJapanese()); + assertEquals("はは", entry.reading); + } + + public void testSimejiSearch() throws Exception { + final Intent i = new Intent(getInstrumentation().getContext(), ResultActivity.class); + i.setAction(ResultActivity.SIMEJI_ACTION_INTERCEPT); + i.putExtra(ResultActivity.SIMEJI_INTENTKEY_REPLACE, "mother"); + tester.startActivity(i); + assertTrue(tester.getText(R.id.textSelectedDictionary).contains("Default")); + final ListView lv = getActivity().getListView(); + assertEquals(1, lv.getCount()); + DictEntry entry = (DictEntry) lv.getItemAtPosition(0); + assertEquals("Searching", entry.english); + Thread.sleep(500); + final Intent i2 = getStartedActivityIntent(); + final List result = (List) i2.getSerializableExtra(ResultActivity.INTENTKEY_RESULT_LIST); + final boolean isSimeji = i2.getBooleanExtra(ResultActivity.INTENTKEY_SIMEJI, false); + entry = result.get(0); + assertEquals("(n) (hum) mother/(P)", entry.english); + assertEquals("母", entry.getJapanese()); + assertEquals("はは", entry.reading); + assertEquals(38, result.size()); + assertEquals(ResultActivity.SIMEJI_ACTION_INTERCEPT, i2.getAction()); + } + + public void testEdictExternSearch() throws Exception { + final Intent i = new Intent(getInstrumentation().getContext(), ResultActivity.class); + i.setAction(ResultActivity.EDICT_ACTION_INTERCEPT); + i.putExtra(ResultActivity.EDICT_INTENTKEY_KANJIS, "空白"); + tester.startActivity(i); + assertTrue(tester.getText(R.id.textSelectedDictionary).contains("Default")); + final ListView lv = getActivity().getListView(); + assertEquals(1, lv.getCount()); + DictEntry entry = (DictEntry) lv.getItemAtPosition(0); + assertEquals("Searching", entry.english); + Thread.sleep(500); + final Intent i2 = getStartedActivityIntent(); + final List result = (List) i2.getSerializableExtra(ResultActivity.INTENTKEY_RESULT_LIST); + entry = result.get(0); + assertEquals("(adj-na,n,adj-no) blank space/vacuum/space/null (NUL)/(P)", entry.english); + assertEquals("空白", entry.getJapanese()); + assertEquals("くうはく", entry.reading); + assertEquals(1, result.size()); + } + + public void testSimejiSearchKanji() throws Exception { + launch(true); + final ListView lv = getActivity().getListView(); + tester.contextMenu(lv, 2, 0); + assertSimejiReturn("母"); + } + + public void testSimejiSearchReading() { + launch(true); + final ListView lv = getActivity().getListView(); + tester.contextMenu(lv, 3, 0); + assertSimejiReturn("はは"); + } + + public void testSimejiSearchEnglish() { + launch(true); + final ListView lv = getActivity().getListView(); + tester.contextMenu(lv, 4, 0); + assertSimejiReturn("(n) (hum) mother/(P)"); + } + + private void assertSimejiReturn(final String expected) { + assertEquals(Activity.RESULT_OK, getFinishedActivityRequest()); + assertEquals(expected, tester.getResultIntent().getStringExtra(ResultActivity.SIMEJI_INTENTKEY_REPLACE)); + } + + public void testSimpleEnglishSearchInTanaka() throws Exception { + final SearchQuery q = new SearchQuery(DictTypeEnum.Tanaka); + q.isJapanese = false; + q.matcher = MatcherEnum.Substring; + q.query = new String[] { "mother" }; + final List result = launch(q); + assertTrue(tester.getText(R.id.textSelectedDictionary).contains("Tanaka")); + final DictEntry entry = result.get(0); + assertEquals("Mother is away from home.", entry.english); + assertEquals("母は留守です。", entry.getJapanese()); + assertEquals("はは は るす です。", entry.reading); + assertEquals(100, result.size()); + } + + public void testMultiwordEnglishSearchInTanaka() throws Exception { + final SearchQuery q = new SearchQuery(DictTypeEnum.Tanaka); + q.isJapanese = false; + q.matcher = MatcherEnum.Substring; + q.query = new String[] { "mother tongue" }; + final List result = launch(q); + assertTrue(tester.getText(R.id.textSelectedDictionary).contains("Tanaka")); + final DictEntry entry = (DictEntry) result.get(0); + assertEquals("My mother tongue is Japanese.", entry.english); + assertEquals("私の母語は日本語です。", entry.getJapanese()); + assertEquals(RomanizationEnum.Hepburn.toHiragana("watashino bogo ha nihongo desu。"), entry.reading); + assertEquals(15, result.size()); + } + + public void testComplexJapaneseSearchInTanaka() throws Exception { + final SearchQuery q = new SearchQuery(DictTypeEnum.Tanaka); + q.isJapanese = true; + q.matcher = MatcherEnum.Substring; + q.query = new String[] { "母", "はは" }; + final List result = launch(q); + assertTrue(tester.getText(R.id.textSelectedDictionary).contains("Tanaka")); + final DictEntry entry = (DictEntry) result.get(0); + assertEquals("Mother has old-fashioned ideas.", entry.english); + assertEquals("母は頭が古い。", entry.getJapanese()); + assertEquals(RomanizationEnum.Hepburn.toHiragana("haha ha atama ga furui。"), entry.reading); + assertEquals(100, result.size()); + } + + public void testComplexJapaneseAndSearch() throws Exception { + final SearchQuery q = new SearchQuery(DictTypeEnum.Edict); + q.isJapanese = true; + q.matcher = MatcherEnum.Substring; + q.query = new String[] {"はは AND 父" }; + final List result = launch(q); + assertTrue(tester.getText(R.id.textSelectedDictionary).contains("Default")); + final DictEntry entry = result.get(0); + assertEquals("(n) father and mother/parents", entry.english); + assertEquals("父母", entry.getJapanese()); + assertEquals("ちちはは", entry.reading); + assertEquals(1, result.size()); + } + + public void testSodAnalysis() { + launch(); + tester.contextMenu(getActivity().getListView(), 6, 0); + tester.assertRequestedActivity(StrokeOrderActivity.class); + final String q = getStartedActivityIntent().getStringExtra(StrokeOrderActivity.INTENTKEY_KANJILIST); + assertEquals("母", q); + } +} diff --git a/src/resources/UnknownTest/src/test/java/net/cyclestreets/api/client/RetrofitApiClientIntegrationTest.java b/src/resources/UnknownTest/src/test/java/net/cyclestreets/api/client/RetrofitApiClientIntegrationTest.java new file mode 100644 index 0000000..35eb727 --- /dev/null +++ b/src/resources/UnknownTest/src/test/java/net/cyclestreets/api/client/RetrofitApiClientIntegrationTest.java @@ -0,0 +1,191 @@ +package net.cyclestreets.api.client; + +import android.content.Context; + +import net.cyclestreets.api.Blog; +import net.cyclestreets.api.Feedback; +import net.cyclestreets.api.GeoPlace; +import net.cyclestreets.api.GeoPlaces; +import net.cyclestreets.api.POI; +import net.cyclestreets.api.POICategories; +import net.cyclestreets.api.POICategory; +import net.cyclestreets.api.Photo; +import net.cyclestreets.api.PhotomapCategories; +import net.cyclestreets.api.Photos; +import net.cyclestreets.api.Registration; +import net.cyclestreets.api.Result; +import net.cyclestreets.api.Signin; +import net.cyclestreets.api.Upload; +import net.cyclestreets.api.UserJourney; +import net.cyclestreets.api.UserJourneys; + +import org.apache.commons.io.IOUtils; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Random; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +// Useful for manual testing that operations do work with the real API, and not just WireMock. +// If we assigned an appropriate api key, these tests could be expanded and un-ignored. +@Ignore +public class RetrofitApiClientIntegrationTest { + + RetrofitApiClient apiClient; + + @Before + public void setUp() throws Exception { + Context testContext = mock(Context.class); + when(testContext.getCacheDir()).thenReturn(new File("/tmp")); + apiClient = new RetrofitApiClient.Builder() + .withApiKey(getApiKey()) + .withContext(testContext) + .withV1Host("https://www.cyclestreets.net") + .withV2Host("https://api.cyclestreets.net") + .build(); + } + + private String getApiKey() throws IOException { + String apiKey = "apiKeyRedacted"; + InputStream in = RetrofitApiClientIntegrationTest.class.getClassLoader().getResourceAsStream("api.key"); + if (in != null) { + try { + apiKey = IOUtils.toString(in, "UTF-8"); + } catch (IOException e) { + // Give up and use default + } finally { + in.close(); + } + } + return apiKey; + } + + @Test + public void hitGeoCoderApi() throws Exception { + GeoPlaces geoPlaces = apiClient.geoCoder("High", 0.1, 52.2, 0.2, 52.3); + for (GeoPlace place : geoPlaces) { + System.out.println(place); + } + } + + @Test + public void hitGetPOICategoriesApi() throws Exception { + POICategories poiCategories = apiClient.getPOICategories(16); + for (POICategory category : poiCategories) { + System.out.println(category.name() + ": " + category); + } + } + + @Test + public void hitGetPOIsByBboxApi() throws Exception { + List pois = apiClient.getPOIs("bikeshops", 0.1, 52.2, 0.2, 52.3); + System.out.println(pois); + } + + @Test + public void hitGetPOIsByRadiusApi() throws Exception { + List pois = apiClient.getPOIs("bikeshops", -1, 54, 100); + System.out.println(pois); + } + + @Test + public void hitGetPhotosApi() throws Exception { + Photos photos = apiClient.getPhotos(0.1, 52.2, 0.2, 52.3); + for (Photo photo : photos) { + System.out.println(photo); + } + } + + @Test + public void hitGetUserJourneysApi() throws Exception { + UserJourneys userJourneys = apiClient.getUserJourneys("socrates"); + for (UserJourney journey : userJourneys) { + System.out.println(journey); + } + } + + @Test + public void hitRegistrationApi() throws Exception { + // Apologies for the test users that this method generates - we should probably delete them... + String random = String.valueOf(new Random().nextInt(100000)); + System.out.println("Registering user test" + random); + Result result = apiClient.register("test" + random, "pwd1234", "friendlyname", "test" + random + "@nosuchdomain.com"); + System.out.println(result.ok()); + System.out.println(result.message()); + assertThat(result.ok(), is(true)); + } + + @Test + public void hitAuthenticateApi() throws Exception { + Signin.Result result = apiClient.authenticate("test66137", "pwd1234"); + System.out.println(result.ok()); + System.out.println(result.name()); + System.out.println(result.email()); + System.out.println(result.error()); + assertThat(result.ok(), is(true)); + } + + @Test + public void hitSendFeedbackApi() throws Exception { + Result result = apiClient.sendFeedback(1234, "test comment", "test", "test@nosuchdomain.com"); + System.out.println(result.ok()); + System.out.println(result.message()); + assertThat(result.ok(), is(true)); + } + + @Test + public void hitGetPhotomapCategoriesApi() throws Exception { + PhotomapCategories categories = apiClient.getPhotomapCategories(); + System.out.println("categories: " + categories.categories()); + System.out.println("meta-categories: " + categories.metaCategories()); + } + + @Test + public void hitUploadPhotoApiWithoutPhoto() throws Exception { + Upload.Result result = apiClient.uploadPhoto("test66137", "pwd1234", 0, 52, 1467394411, + "cycleparking", "good", "Caption: THIS IS TEST DATA and should not be on the map", null); + System.out.println(result.ok()); + System.out.println(result.url()); + System.out.println(result.error()); + // Important - remove the test data from the map, otherwise we look pretty unprofessional! + System.out.println("Don't forgot to log on as this user and delete the photo afterwards..."); + } + + @Test + public void hitUploadPhotoApiWithPhoto() throws Exception { + Upload.Result result = apiClient.uploadPhoto("test66137", "pwd1234", 0, 52, 1467394411, + "cycleparking", "good", "Caption: THIS IS TEST DATA and should not be on the map", "/tmp/test-image.png"); + System.out.println(result.ok()); + System.out.println(result.url()); + System.out.println(result.error()); + // Important - remove the test data from the map, otherwise we look pretty unprofessional! + System.out.println("Don't forgot to log on as this user and delete the photo afterwards..."); + } + + @Test + public void hitGetJourneyXmlApi() throws Exception { + String xml = apiClient.getJourneyXml("quietest", "0.117950,52.205302,City+Centre|0.131402,52.221046,Mulberry+Close|0.147324,52.199650,Thoday+Street", null, null, 24); + System.out.println(xml); + } + + @Test + public void hitRetrievePreviousJourneyXmlApi() throws Exception { + String xml = apiClient.retrievePreviousJourneyXml("fastest", 53135357); + System.out.println(xml); + } + + @Test + public void hitGetBlogEntriesApi() throws Exception { + Blog blog = apiClient.getBlogEntries(); + System.out.println(blog.toHtml()); + } +} diff --git a/src/resources/test-smells.properties b/src/resources/test-smells.properties new file mode 100644 index 0000000..d980f6d --- /dev/null +++ b/src/resources/test-smells.properties @@ -0,0 +1 @@ +report.granularity=FILE,CLASS,METHOD \ No newline at end of file diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/IntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/IntegrationTest.java new file mode 100644 index 0000000..5fa153c --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/IntegrationTest.java @@ -0,0 +1,14 @@ +package edu.rit.se.testsmells.testsmell; + +import org.junit.jupiter.api.Tag; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Tag("integration") +public @interface IntegrationTest { +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java new file mode 100644 index 0000000..f12e73e --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java @@ -0,0 +1,200 @@ +package edu.rit.se.testsmells.testsmell; + +import com.github.javaparser.JavaParser; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class MethodValidatorTest { + MethodValidator sut; + + @BeforeEach + void setUp() { + sut = MethodValidator.getInstance(); + } + + @Test + void isValidTestMethod_ignored() { + String code = + "class nothing { \n" + + "@Ignore \n" + + "@Test \n" + + "public void sampleTest(){} \n" + + "} \n"; + + MethodVisitorStub assertion = new MethodVisitorStub(new TestMethodAssertionCommand(false)::invoke); + + assertion.visit(JavaParser.parse(code), null); + } + + @Test + void isValidTestMethod_private() { + String code = + "class nothing { \n" + + "@Test \n" + + "private void sampleTest(){} \n" + + "} \n"; + + MethodVisitorStub assertion = new MethodVisitorStub(new TestMethodAssertionCommand(false)::invoke); + + assertion.visit(JavaParser.parse(code), null); + } + + @Test + void isValidTestMethod_annotated() { + String code = + "class nothing { \n" + + "@Test \n" + + "public void sampleTest(){} \n" + + "} \n"; + + MethodVisitorStub assertion = new MethodVisitorStub(new TestMethodAssertionCommand(true)::invoke); + + assertion.visit(JavaParser.parse(code), null); + } + + @Test + void isValidTestMethod_startsWithTest() { + String code = + "class nothing { \n" + + "public void testSample(){} \n" + + "} \n"; + + MethodVisitorStub assertion = new MethodVisitorStub(new TestMethodAssertionCommand(true)::invoke); + + assertion.visit(JavaParser.parse(code), null); + } + + @Test + void isValidTestMethod_visibleNonTestMethod() { + String code = + "class nothing { \n" + + "public void sampleTest(){} \n" + + "} \n"; + + MethodVisitorStub assertion = new MethodVisitorStub(new TestMethodAssertionCommand(false)::invoke); + + assertion.visit(JavaParser.parse(code), null); + } + + @Test + void isValidSetupMethod_ignored() { + String code = + "class nothing { \n" + + "@Ignore \n" + + "@Before \n" + + "public void setUp(){} \n" + + "} \n"; + + MethodVisitorStub assertion = new MethodVisitorStub(new SetupMethodAssertionCommand(false)::invoke); + + assertion.visit(JavaParser.parse(code), null); + } + + @Test + void isValidSetupMethod_private() { + String code = + "class nothing { \n" + + "@Before \n" + + "private void setUp(){} \n" + + "} \n"; + + MethodVisitorStub assertion = new MethodVisitorStub(new SetupMethodAssertionCommand(false)::invoke); + + assertion.visit(JavaParser.parse(code), null); + } + + @Test + void isValidSetupMethod_annotated() { + String code = + "class nothing { \n" + + "@Before \n" + + "public void setSut(){} \n" + + "} \n"; + + MethodVisitorStub assertion = new MethodVisitorStub(new SetupMethodAssertionCommand(true)::invoke); + + assertion.visit(JavaParser.parse(code), null); + } + + @Test + void isValidSetupMethod_annotatedAlt() { + String code = + "class nothing { \n" + + "@BeforeEach \n" + + "public void setSut(){} \n" + + "} \n"; + + MethodVisitorStub assertion = new MethodVisitorStub(new SetupMethodAssertionCommand(true)::invoke); + + assertion.visit(JavaParser.parse(code), null); + } + + @Test + void isValidSetupMethod_namedSetUp() { + String code = + "class nothing { \n" + + "public void setUp(){} \n" + + "} \n"; + + MethodVisitorStub assertion = new MethodVisitorStub(new SetupMethodAssertionCommand(true)::invoke); + + assertion.visit(JavaParser.parse(code), null); + } + + @Test + void isValidSetupMethod_visibleButIsTestMethod() { + String code = + "class nothing { \n" + + "@Test \n" + + "public void sampleTest(){} \n" + + "} \n"; + + MethodVisitorStub assertion = new MethodVisitorStub(new SetupMethodAssertionCommand(false)::invoke); + + assertion.visit(JavaParser.parse(code), null); + } + + static class MethodVisitorStub extends VoidVisitorAdapter { + Consumer func; + + public MethodVisitorStub(Consumer func) { + this.func = func; + } + + @Override + public void visit(MethodDeclaration n, Void arg) { + func.accept(n); + super.visit(n, arg); + } + } + + private class TestMethodAssertionCommand { + private final boolean assertionValue; + + private TestMethodAssertionCommand(boolean assertionValue) { + this.assertionValue = assertionValue; + } + + public void invoke(MethodDeclaration a) { + assertEquals(assertionValue, sut.isValidTestMethod(a)); + } + } + + private class SetupMethodAssertionCommand { + private final boolean assertionValue; + + private SetupMethodAssertionCommand(boolean assertionValue) { + this.assertionValue = assertionValue; + } + + public void invoke(MethodDeclaration a) { + assertEquals(assertionValue, sut.isValidSetupMethod(a)); + } + } +} \ No newline at end of file diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java new file mode 100644 index 0000000..513d46a --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java @@ -0,0 +1,201 @@ +package edu.rit.se.testsmells.testsmell; + +import edu.rit.se.testsmells.testsmell.smell.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@IntegrationTest +public class MultiFilesIntegrationTest { + + private TestSmellDetector testSmellDetector; + private List outputFiles; + + @BeforeEach + void setUp() { + outputFiles = new ArrayList<>(); + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new AssertionRoulette()); + testSmellDetector.addDetectableSmell(new ConditionalTestLogic()); + testSmellDetector.addDetectableSmell(new ConstructorInitialization()); + testSmellDetector.addDetectableSmell(new DefaultTest()); + testSmellDetector.addDetectableSmell(new EmptyTest()); + testSmellDetector.addDetectableSmell(new ExceptionCatchingThrowing()); + testSmellDetector.addDetectableSmell(new GeneralFixture()); + testSmellDetector.addDetectableSmell(new PrintStatement()); + testSmellDetector.addDetectableSmell(new RedundantAssertion()); + testSmellDetector.addDetectableSmell(new MysteryGuest()); + testSmellDetector.addDetectableSmell(new SensitiveEquality()); + testSmellDetector.addDetectableSmell(new VerboseTest()); + testSmellDetector.addDetectableSmell(new SleepyTest()); + testSmellDetector.addDetectableSmell(new EagerTest()); + testSmellDetector.addDetectableSmell(new LazyTest()); + testSmellDetector.addDetectableSmell(new DuplicateAssert()); + testSmellDetector.addDetectableSmell(new UnknownTest()); + testSmellDetector.addDetectableSmell(new IgnoredTest()); + testSmellDetector.addDetectableSmell(new ResourceOptimism()); + testSmellDetector.addDetectableSmell(new MagicNumberTest()); + testSmellDetector.addDetectableSmell(new DependentTest()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + outputFiles.forEach(File::delete); + } + + @Test + public void testTwoMethodOrientedSmells() throws IOException { + List testFiles = Arrays.asList( + new TestFile( + "RedundantPrintProject", + "/RedundantPrint/src/test/java/org/hwyl/sexytopo/control/util/Space3DTransformerTest.java", + "" + ), + new TestFile( + "DuplicateAssertProject", + "/DuplicateAssert/src/test/java/org/openbmap/utils/XmlSanitizerTest.java", + "" + )); + + List reportsSize = detectAndReportLOC(testFiles); + + // Method, class, and file should not be filtered + assertEquals(3, reportsSize.size()); + // 1 DuplicateAssert test case + 7 RedundantPrint test case + header + assertEquals(9, reportsSize.get(0)); + // 1 DuplicateAssert test class + 1 RedundantPrint test class + header + assertEquals(3, reportsSize.get(1)); + // 1 DuplicateAssert test file + 1 RedundantPrint test file + header + assertEquals(3, reportsSize.get(2)); + } + + @Test + public void testTwoClassOrientedSmells() throws IOException { + List testFiles = Arrays.asList( + new TestFile( + "DefaultTestProject", + "/DefaultTest/src/test/java/com/app/missednotificationsreminder/ExampleUnitTest.java", + "" + ), + new TestFile( + "ConstructorInitializationProject", + "/ConstructorInitialization/src/test/java/org/briarproject/bramble/crypto/TagEncodingTest.java", + "" + )); + + List reportsSize = detectAndReportLOC(testFiles); + + // Method, class, and file should not be filtered + assertEquals(3, reportsSize.size()); + // 1 DuplicateAssert test case + 7 RedundantPrint test case + header + assertEquals(6, reportsSize.get(0)); + // 10 DefaultTest test classes (1 top level + 9 inner classes) + 1 ConstructorInitialization test class + header + assertEquals(11, reportsSize.get(1)); + // 1 DuplicateAssert test file + 1 RedundantPrint test file + header + assertEquals(3, reportsSize.get(2)); + } + + @Test + public void testBothClassAndMethodOrientedSmells() throws IOException { + List testFiles = Arrays.asList( + new TestFile( + "DefaultTestProject", + "/DefaultTest/src/test/java/com/app/missednotificationsreminder/ExampleUnitTest.java", + "" + ), + new TestFile( + "DuplicateAssertProject", + "/DuplicateAssert/src/test/java/org/openbmap/utils/XmlSanitizerTest.java", + "" + )); + + List reportsSize = detectAndReportLOC(testFiles); + + // Method, class, and file should not be filtered + assertEquals(3, reportsSize.size()); + // 1 DuplicateAssert test case + 7 RedundantPrint test case + header + assertEquals(4, reportsSize.get(0)); + // 10 DefaultTest test classes (1 top level + 9 inner classes) + 1 DuplicateAssert test class + header + assertEquals(11, reportsSize.get(1)); + // 1 DuplicateAssert test file + 1 RedundantPrint test file + header + assertEquals(3, reportsSize.get(2)); + } + + @Test + public void testBothClassAndMethodOrientedSmells_csvColumns() throws IOException { + List testFiles = Arrays.asList( + new TestFile( + "DefaultTestProject", + "/DefaultTest/src/test/java/com/app/missednotificationsreminder/ExampleUnitTest.java", + "" + ), + new TestFile( + "DuplicateAssertProject", + "/DuplicateAssert/src/test/java/org/openbmap/utils/XmlSanitizerTest.java", + "" + )); + + detectAndReportLOC(testFiles); + + assertEquals(3, outputFiles.size(), "Not generated all three reports"); + + String expectedMethodHeader = "Element Name,WhileCount,ConditionCount,RedundantCount,BadAssertCount,TotalAssertCount,IfCount,ExceptionCount,ForeachCount,PrintCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,SensitiveCount,MagicNumberCount,Assertion Roulette,Mystery Guest,Sleepy Test,Unknown Test,Redundant Assertion,Dependent Test,Magic Number Test,Conditional Test Logic,EmptyTest,General Fixture,Sensitive Equality,Verbose Test,IgnoredTest,Resource Optimism,Duplicate Assert,Exception Catching Throwing,Print Statement"; + String expectedFileHeader = "App,ProductionFileName,TestFilePath,TestFileName,RelativeProductionFilePath,RelativeTestFilePath,ProductionFilePath,Assertion Roulette,Conditional Test Logic,Constructor Initialization,Default Test,EmptyTest,Exception Catching Throwing,General Fixture,Print Statement,Redundant Assertion,Mystery Guest,Sensitive Equality,Verbose Test,Sleepy Test,Eager Test,Lazy Test,Duplicate Assert,Unknown Test,IgnoredTest,Resource Optimism,Magic Number Test,Dependent Test"; + String expectedClassHeader = "Element Name,IgnoredTest,Constructor Initialization,Default Test"; + + List expectedCounts = Stream.of(expectedMethodHeader,expectedClassHeader,expectedFileHeader).map(s-> Arrays.stream(s.split(",")).count()).collect(Collectors.toList()); + + for (int i = 0; i < outputFiles.size(); i++) { + BufferedReader reader = new BufferedReader(new FileReader(outputFiles.get(i))); + List colCounts = reader.lines().map(line -> Arrays.stream(line.split(",")).count()).distinct().collect(Collectors.toList()); + assertEquals(1, colCounts.size(), "Lines with different number of columns"); + Long[] expected = {expectedCounts.get(i)}; + assertArrayEquals(expected,colCounts.toArray()); + } + } + + private List detectAndReportLOC(List testFiles) throws IOException { + CSVWriter csvWriter = CSVWriter.createResultsWriter(); + ReportController reportCtrl = ReportController.createReportController(csvWriter); + + for (TestFile file : testFiles) { + testSmellDetector.detectSmells(file); + } + + reportCtrl.report(testFiles); + + outputFiles = Stream.of( + ReportController.ReportGranularity.METHOD, + ReportController.ReportGranularity.CLASS, + ReportController.ReportGranularity.FILE + ) + .map(granularity -> granularity.toString() + "_" + csvWriter.getSuffix()) + .map(File::new) + .collect(Collectors.toList()); + List reportsSize = outputFiles.stream() + .filter(File::exists).filter(f -> !f.isDirectory()) + .map(file -> { + try { + return (int) new BufferedReader(new FileReader(file)).lines().count(); + } catch (FileNotFoundException e) { // Never occurs + e.printStackTrace(); + return 0; + } + }) + .collect(Collectors.toList()); + return reportsSize; + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerClassIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerClassIntegrationTest.java new file mode 100644 index 0000000..dcbc413 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerClassIntegrationTest.java @@ -0,0 +1,59 @@ +package edu.rit.se.testsmells.testsmell; + +import edu.rit.se.testsmells.testsmell.smell.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +class ReportControllerClassIntegrationTest { + private File outputFile; + private TestFile file; + private ReportController sut; + private CSVWriter csvWriter; + private List smells = Arrays.asList(new AssertionRoulette(), new ConditionalTestLogic(), new ConstructorInitialization(), new DefaultTest(), new EmptyTest(), new ExceptionCatchingThrowing(), new GeneralFixture(), new MysteryGuest(), new PrintStatement(), new RedundantAssertion(), new SensitiveEquality(), new VerboseTest(), new SleepyTest(), new EagerTest(), new LazyTest(), new DuplicateAssert(), new UnknownTest(), new IgnoredTest(), new ResourceOptimism(), new MagicNumberTest(), new DependentTest()); + private String appName = "ConstructorInitializationProject"; + private String testFilePath = "/ConstructorInitialization/src/test/java/org/briarproject/bramble/crypto/TagEncodingTest.java"; + private String productionFilePath = ""; + + @BeforeEach + void setUp() throws IOException { + TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); + smells.forEach(testSmellDetector::addDetectableSmell); + file = new TestFile(appName, testFilePath, productionFilePath); + csvWriter = CSVWriter.createResultsWriter(); + testSmellDetector.detectSmells(file); + } + + @AfterEach + void tearDown() { + //noinspection ResultOfMethodCallIgnored + outputFile.delete(); + } + + + @Test + void testCLASSOutput() throws IOException { + List outputs = Arrays.asList(ReportController.ReportGranularity.CLASS); + sut = new ReportController(csvWriter, outputs); + sut.report(Collections.singletonList(file)); + + outputFile = new File("CLASS_" + csvWriter.getSuffix()); + assertTrue(outputFile.exists(), "Class output file missing!"); + List fileContent = new BufferedReader(new FileReader(outputFile)).lines().collect(Collectors.toList()); + assertEquals(2, fileContent.size(), "File with unexpected size received! File's content is: " + System.lineSeparator() + String.join(System.lineSeparator(), fileContent) + System.lineSeparator()); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerFileIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerFileIntegrationTest.java new file mode 100644 index 0000000..718d66e --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerFileIntegrationTest.java @@ -0,0 +1,95 @@ +package edu.rit.se.testsmells.testsmell; + +import edu.rit.se.testsmells.testsmell.smell.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.*; + +@IntegrationTest +class ReportControllerFileIntegrationTest { + private File outputFile; + private TestFile file; + private ReportController sut; + private CSVWriter csvWriter; + private List smells = Arrays.asList(new AssertionRoulette(), new ConditionalTestLogic(), new ConstructorInitialization(), new DefaultTest(), new EmptyTest(), new ExceptionCatchingThrowing(), new GeneralFixture(), new MysteryGuest(), new PrintStatement(), new RedundantAssertion(), new SensitiveEquality(), new VerboseTest(), new SleepyTest(), new EagerTest(), new LazyTest(), new DuplicateAssert(), new UnknownTest(), new IgnoredTest(), new ResourceOptimism(), new MagicNumberTest(), new DependentTest()); + private String appName = "LazyTest"; + private String testFilePath = "/LazyTest/src/test/java/com/github/marmaladesky/tests/CryptographerTest.java"; + private String productionFilePath = "/LazyTest/src/main/java/com/github/marmaladesky/Cryptographer.java"; + + @BeforeEach + void setUp() throws IOException { + TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); + smells.forEach(testSmellDetector::addDetectableSmell); + file = new TestFile(appName, testFilePath, productionFilePath); + csvWriter = CSVWriter.createResultsWriter(); + testSmellDetector.detectSmells(file); + sut = new ReportController(csvWriter, Collections.singletonList(ReportController.ReportGranularity.FILE)); + sut.report(Collections.singletonList(file)); + outputFile = new File(csvWriter.getFilename()); + assertTrue(outputFile.exists(), "Output file missing!"); + } + + @AfterEach + void tearDown() { + //noinspection ResultOfMethodCallIgnored + outputFile.delete(); + } + + @Test + void testReportExists() { + assertTrue(outputFile.exists(), "Output file missing!"); + } + + @Test + void testReportDoestNotThrow() { + sut = new ReportController(csvWriter, Collections.singletonList(ReportController.ReportGranularity.FILE)); + assertDoesNotThrow(() -> sut.report(Collections.singletonList(file))); + outputFile = new File(csvWriter.getFilename()); + } + + @Test + void testHeader_FILE() throws IOException { + List headerEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).readLine().split(",")); + + List expectedEntries = new ArrayList<>(Arrays.asList("App", "ProductionFileName", "TestFilePath", "TestFileName", "RelativeProductionFilePath", "RelativeTestFilePath", "ProductionFilePath")); + List smellsName = smells.stream().map(AbstractSmell::getSmellName).collect(Collectors.toList()); + expectedEntries.addAll(smellsName); + + assertIterableEquals(headerEntries, expectedEntries); + } + + @Test + void testContent_FILE() throws IOException { + List contentEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).lines().skip(1).findFirst().orElse("").split(",")); + + List expectedEntries = new ArrayList<>(Arrays.asList(appName, "Cryptographer.java", testFilePath, "CryptographerTest.java", "src/main/java/com/github/marmaladesky/Cryptographer.java", "src/test/java/com/github/marmaladesky/tests/CryptographerTest.java", productionFilePath)); + + List expectedSmells = Arrays.asList("Lazy Test", "Eager Test", "Exception Catching Throwing"); + List hasSmell = smells.stream().map(x -> expectedSmells.contains(x.getSmellName()) ? "true" : "false").collect(Collectors.toList()); + + expectedEntries.addAll(hasSmell); + + assertIterableEquals(expectedEntries, contentEntries); + } + + @Test + void testConsistentColumns() throws IOException { + BufferedReader content = new BufferedReader(new FileReader(outputFile)); + int nCols = content.readLine().split(",").length; + + assertEquals(nCols, content.readLine().split(",").length); + assertEquals(nCols, 7 + smells.size()); + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java new file mode 100644 index 0000000..9f1850c --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java @@ -0,0 +1,101 @@ +package edu.rit.se.testsmells.testsmell; + +import edu.rit.se.testsmells.testsmell.smell.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +class ReportControllerMethodIntegrationTest { + private File outputFile; + private TestFile file; + private ReportController sut; + private CSVWriter csvWriter; + private List smells = Arrays.asList(new AssertionRoulette(), new ConditionalTestLogic(), new ConstructorInitialization(), new DefaultTest(), new EmptyTest(), new ExceptionCatchingThrowing(), new GeneralFixture(), new MysteryGuest(), new PrintStatement(), new RedundantAssertion(), new SensitiveEquality(), new VerboseTest(), new SleepyTest(), new EagerTest(), new LazyTest(), new DuplicateAssert(), new UnknownTest(), new IgnoredTest(), new ResourceOptimism(), new MagicNumberTest(), new DependentTest()); + private String appName = "LazyTest"; + private String testFilePath = "/LazyTest/src/test/java/com/github/marmaladesky/tests/CryptographerTest.java"; + private String productionFilePath = "/LazyTest/src/main/java/com/github/marmaladesky/Cryptographer.java"; + + @BeforeEach + void setUp() throws IOException { + TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); + smells.forEach(testSmellDetector::addDetectableSmell); + file = new TestFile(appName, testFilePath, productionFilePath); + csvWriter = CSVWriter.createResultsWriter(); + testSmellDetector.detectSmells(file); + sut = new ReportController(csvWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); + sut.report(Collections.singletonList(file)); + outputFile = new File(csvWriter.getFilename()); + } + + @AfterEach + void tearDown() { + //noinspection ResultOfMethodCallIgnored + outputFile.delete(); + } + + @Test + void testReportExists() { + assertTrue(outputFile.exists(), "Output file missing!"); + } + + List getMethodSmells() { + List methodSmells = new ArrayList<>(); + for (AbstractSmell smell : file.getTestSmells()) { + long nTestMethods = smell.getSmellyElements().stream().filter(elem -> elem instanceof TestMethod).count(); + if (nTestMethods != 0) { + methodSmells.add(smell); + } + } + return methodSmells; + } + + @Test + void testReport_METHOD() throws IOException { + List entries = new BufferedReader(new FileReader(outputFile)).lines().collect(Collectors.toList()); + + + assertEquals("Element Name,WhileCount,ConditionCount,RedundantCount,IfCount,ExceptionCount,ForeachCount,PrintCount,TotalAssertCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,BadAssertCount,SensitiveCount,MagicNumberCount,Assertion Roulette,Eager Test,Mystery Guest,Sleepy Test,Unknown Test,Redundant Assertion,Dependent Test,Magic Number Test,Conditional Test Logic,EmptyTest,General Fixture,Sensitive Equality,Verbose Test,IgnoredTest,Resource Optimism,Duplicate Assert,Exception Catching Throwing,Print Statement,Lazy Test", entries.get(0)); + assertEquals("com.github.marmaladesky.tests.CryptographerTest.testDecrypt,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,true", entries.get(1)); + assertEquals("com.github.marmaladesky.tests.CryptographerTest.testEncrypt,0,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,true", entries.get(2)); + } + + @Test + void testHeader_METHOD() throws IOException { + List headerEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).readLine().split(",")); + + assertEquals("Element Name,WhileCount,ConditionCount,RedundantCount,IfCount,ExceptionCount,ForeachCount,PrintCount,TotalAssertCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,BadAssertCount,SensitiveCount,MagicNumberCount,Assertion Roulette,Eager Test,Mystery Guest,Sleepy Test,Unknown Test,Redundant Assertion,Dependent Test,Magic Number Test,Conditional Test Logic,EmptyTest,General Fixture,Sensitive Equality,Verbose Test,IgnoredTest,Resource Optimism,Duplicate Assert,Exception Catching Throwing,Print Statement,Lazy Test", String.join(",", headerEntries)); + } + + @Test + void testContent_METHOD_Decrypt() throws IOException { + assertEquals(19, getMethodSmells().size()); + + List contentEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).lines().skip(1).findFirst().orElse("").split(",")); + assertEquals(2, contentEntries.stream().filter(e -> e.equals("true")).count()); // "Lazy Test" and "Exception Catching Throwing" + assertEquals(17, contentEntries.stream().filter(e -> e.equals("false")).count()); // 18 methods smells - 2 detected smells + } + + @Test + void testContent_METHOD_Encrypt() throws IOException { + assertEquals(19, getMethodSmells().size()); + + List contentEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).lines().skip(2).findFirst().orElse("").split(",")); + assertEquals(3, contentEntries.stream().filter(e -> e.equals("true")).count()); // "Eager Test", "Lazy Test" and "Exception Catching Throwing" + assertEquals(16, contentEntries.stream().filter(e -> e.equals("false")).count()); // 18 methods smells - 3 detected smells + } +} + diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMultipleGranularityIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMultipleGranularityIntegrationTest.java new file mode 100644 index 0000000..3ad9d2a --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMultipleGranularityIntegrationTest.java @@ -0,0 +1,76 @@ +package edu.rit.se.testsmells.testsmell; + +import edu.rit.se.testsmells.testsmell.smell.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +class ReportControllerMultipleGranularityIntegrationTest { + private List outputFiles; + private TestFile file; + private ReportController sut; + private CSVWriter csvWriter; + private List smells = Arrays.asList(new AssertionRoulette(), new ConditionalTestLogic(), new ConstructorInitialization(), new DefaultTest(), new EmptyTest(), new ExceptionCatchingThrowing(), new GeneralFixture(), new MysteryGuest(), new PrintStatement(), new RedundantAssertion(), new SensitiveEquality(), new VerboseTest(), new SleepyTest(), new EagerTest(), new LazyTest(), new DuplicateAssert(), new UnknownTest(), new IgnoredTest(), new ResourceOptimism(), new MagicNumberTest(), new DependentTest()); + private String appName = "LazyTest"; + private String testFilePath = "/LazyTest/src/test/java/com/github/marmaladesky/tests/CryptographerTest.java"; + private String productionFilePath = "/LazyTest/src/main/java/com/github/marmaladesky/Cryptographer.java"; + + @BeforeEach + void setUp() throws IOException { + TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); + smells.forEach(testSmellDetector::addDetectableSmell); + file = new TestFile(appName, testFilePath, productionFilePath); + csvWriter = CSVWriter.createResultsWriter(); + testSmellDetector.detectSmells(file); + List outputs = Arrays.asList(ReportController.ReportGranularity.METHOD, ReportController.ReportGranularity.CLASS, ReportController.ReportGranularity.FILE); + sut = new ReportController(csvWriter, outputs); + sut.report(Collections.singletonList(file)); + outputFiles = outputs.stream().map(granularity -> granularity.toString() + "_" + csvWriter.getSuffix()).map(File::new).collect(Collectors.toList()); + } + + @AfterEach + void tearDown() { + //noinspection ResultOfMethodCallIgnored + outputFiles.forEach(File::delete); + } + + @Test + void testMultipleOutput_METHOD() throws IOException { + File outputFile = outputFiles.get(0); + assertTrue(outputFile.exists(), "Method output file missing!"); + List fileContent = new BufferedReader(new FileReader(outputFile)).lines().collect(Collectors.toList()); + assertEquals(3, fileContent.size(), "File with unexpected size received! File's content is: " + System.lineSeparator() + String.join(System.lineSeparator(), fileContent) + System.lineSeparator()); + } + + @Test + void testMultipleOutput_CLASS() throws IOException { + File outputFile = outputFiles.get(1); + assertTrue(outputFile.exists(), "Class output file missing!"); + List fileContent = new BufferedReader(new FileReader(outputFile)).lines().collect(Collectors.toList()); + assertEquals(2, fileContent.size(), "File with unexpected size received! File's content is: " + System.lineSeparator() + String.join(System.lineSeparator(), fileContent) + System.lineSeparator()); + + } + + @Test + void testMultipleOutput_FILE() throws IOException { + File outputFile = outputFiles.get(2); + assertTrue(outputFile.exists(), "File output file missing!"); + List fileContent = new BufferedReader(new FileReader(outputFile)).lines().collect(Collectors.toList()); + assertEquals(2, fileContent.size(), "File with unexpected size received! File's content is: " + System.lineSeparator() + String.join(System.lineSeparator(), fileContent) + System.lineSeparator()); + + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java new file mode 100644 index 0000000..ad1ef69 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java @@ -0,0 +1,63 @@ +package edu.rit.se.testsmells.testsmell; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.mockito.Mockito.*; + +public class ReportControllerTest { + TestFile tf; + AbstractSmell smell; + List files; + ReportController sut; + CSVWriter csvWriter; + + @BeforeEach + public void setUp() throws IOException { + files = new ArrayList<>(); + smell = mock(AbstractSmell.class); + tf = mock(TestFile.class); + csvWriter = mock(CSVWriter.class); + sut = ReportController.createReportController(csvWriter); + } + + @Test + public void testReportDoesNotThrow() { + assertDoesNotThrow(() -> sut.report(files)); + } + + @Test + public void testReportNotCallExportSmells() throws IOException { + sut.report(files); + verify(csvWriter, never()).exportSmells((SmellsContainer) any()); + } + + @Test + public void testReportCallExportSmellsFromFile() throws IOException { + files.add(tf); + when(tf.getTestSmells()).thenReturn(Arrays.asList(smell)); + + sut.report(files); + + verify(csvWriter, atLeastOnce()).exportSmells(tf); + } + + @Test + public void testReportCallExportSmells() throws IOException { + files.add(tf); + TestMethod smellyElem = new TestMethod("anyMethod"); + when(smell.getSmellyElements()).thenReturn(Arrays.asList(smellyElem)); + when(tf.getTestSmells()).thenReturn(Arrays.asList(smell)); + + sut.report(files); + + verify(csvWriter, atLeastOnce()).exportSmells(tf); + //verify(resultsWriter, atLeastOnce()).exportSmells(smellyElem); TODO: make exportSmells(smellyElem) be detectable + } +} diff --git a/src/test/java/testsmell/TestFileTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestFileTest.java similarity index 69% rename from src/test/java/testsmell/TestFileTest.java rename to src/test/java/edu/rit/se/testsmells/testsmell/TestFileTest.java index 54e0d52..b15fe43 100644 --- a/src/test/java/testsmell/TestFileTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestFileTest.java @@ -1,41 +1,36 @@ -package testsmell; +package edu.rit.se.testsmells.testsmell; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnOs; import org.junit.jupiter.api.condition.OS; -import static org.junit.jupiter.api.Assertions.*; +import java.io.File; -class TestFileTest { +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +@EnabledOnOs({OS.LINUX, OS.MAC}) +class UnixTestFileTest { private String fileTest = "commons-lang," + "/Users/grano/projects/commons-lang/src/test/java/org/apache/commons/lang3/RandomStringUtilsTest.java," + "/Users/grano/projects/commons-lang/src/main/java/org/apache/commons/lang3/RandomStringUtils.java"; - private String fileTestWindows = "myCoolApp," + - "F:\\Apps\\myCoolApp\\code\\test\\GraphTest.java," + - "F:\\Apps\\myCoolApp\\code\\src\\Graph.java"; private TestFile testFileUnix; - private TestFile testFileWindows; @BeforeEach void setUp() { String[] splits = fileTest.split(","); testFileUnix = new TestFile(splits[0], splits[1], splits[2]); - String[] splitW = fileTestWindows.split(","); - testFileWindows = new TestFile(splitW[0], splitW[1], splitW[2]); } @Test - @EnabledOnOs({OS.WINDOWS}) - public void testGetFileNameWindows() { - String oracle = "GraphTest.java"; - String output = testFileWindows.getTestFileName(); + public void testGetProductionFileNameUnix() { + String oracle = "RandomStringUtils.java"; + String output = testFileUnix.getProductionFileName(); assertEquals(oracle, output); } @Test - @EnabledOnOs({OS.LINUX, OS.MAC}) public void testGetFileNameUnix() { String oracle = "RandomStringUtilsTest.java"; String output = testFileUnix.getTestFileName(); @@ -43,34 +38,47 @@ public void testGetFileNameUnix() { } @Test - @EnabledOnOs({OS.WINDOWS}) - public void testProductionFileNameWindows() { - String oracle = "Graph.java"; - String output = testFileWindows.getProductionFileName(); + public void testGetRelativeProductionFilePathUnix() { + String oracle = "src/main/java/org/apache/commons/lang3/RandomStringUtils.java"; + String output = testFileUnix.getRelativeProductionFilePath(); assertEquals(oracle, output); } @Test - @EnabledOnOs({OS.LINUX, OS.MAC}) - public void testGetProductionFileNameUnix() { - String oracle = "RandomStringUtils.java"; - String output = testFileUnix.getProductionFileName(); + public void testGetRelativeTestFilePathUnix() { + String oracle = "src/test/java/org/apache/commons/lang3/RandomStringUtilsTest.java"; + String output = testFileUnix.getRelativeTestFilePath(); assertEquals(oracle, output); } +} + +@EnabledOnOs({OS.WINDOWS}) +class WindowsTestFileTest { + + private String fileTestWindows = "myCoolApp," + + "F:\\Apps\\myCoolApp\\code\\test\\GraphTest.java," + + "F:\\Apps\\myCoolApp\\code\\src\\Graph.java"; + private TestFile testFileWindows; + + @BeforeEach + void setUp() { + String[] split = fileTestWindows.split(","); + testFileWindows = new TestFile(split[0], split[1], split[2]); + } @Test - @EnabledOnOs({OS.LINUX, OS.MAC}) - public void testGetRelativeProductionFilePathUnix() { - String oracle = "src/main/java/org/apache/commons/lang3/RandomStringUtils.java"; - String output = testFileUnix.getRelativeProductionFilePath(); + @EnabledOnOs({OS.WINDOWS}) + public void testProductionFileNameWindows() { + String oracle = "Graph.java"; + String output = testFileWindows.getProductionFileName(); assertEquals(oracle, output); } @Test - @EnabledOnOs({OS.LINUX, OS.MAC}) - public void testGetRelativeTestFilePathUnix() { - String oracle = "src/test/java/org/apache/commons/lang3/RandomStringUtilsTest.java"; - String output = testFileUnix.getRelativeTestFilePath(); + @EnabledOnOs({OS.WINDOWS}) + public void testGetFileNameWindows() { + String oracle = "GraphTest.java"; + String output = testFileWindows.getTestFileName(); assertEquals(oracle, output); } @@ -89,4 +97,32 @@ public void testGetRelativeTestFilePathWindows() { String output = testFileWindows.getRelativeTestFilePath(); assertEquals(oracle, output); } +} + +class TestFileSpecialCasesTest { + String path; + String app; + + @BeforeEach + public void setUp() { + app = "project"; + } + + @Test + public void testFilePathWithoutSeparatorTest() { + path = "file.extension"; + assertThrows(IllegalArgumentException.class, () -> new TestFile(app, path, path)); + } + + @Test + public void testFileWithoutExtension() { + path = app + File.separator + "to" + File.separator + "fileWithoutExtension"; + assertThrows(IllegalArgumentException.class, () -> new TestFile(app, path, path)); + } + + @Test + public void testEmptyAppName() { + path = "to" + File.separator + "fileWithoutExtension"; + assertThrows(IllegalArgumentException.class, () -> new TestFile("", path, path)); + } } \ No newline at end of file diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIntegrationTest.java new file mode 100644 index 0000000..f892576 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIntegrationTest.java @@ -0,0 +1,66 @@ +package edu.rit.se.testsmells.testsmell; + +import edu.rit.se.testsmells.testsmell.smell.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +@IntegrationTest +public class TestSmellDetectorIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new AssertionRoulette()); + testSmellDetector.addDetectableSmell(new ConditionalTestLogic()); + + testSmellDetector.addDetectableSmell(new ConstructorInitialization()); + testSmellDetector.addDetectableSmell(new DefaultTest()); + testSmellDetector.addDetectableSmell(new EmptyTest()); + testSmellDetector.addDetectableSmell(new ExceptionCatchingThrowing()); + testSmellDetector.addDetectableSmell(new GeneralFixture()); + testSmellDetector.addDetectableSmell(new PrintStatement()); + testSmellDetector.addDetectableSmell(new RedundantAssertion()); + testSmellDetector.addDetectableSmell(new MysteryGuest()); + testSmellDetector.addDetectableSmell(new SensitiveEquality()); + testSmellDetector.addDetectableSmell(new VerboseTest()); + testSmellDetector.addDetectableSmell(new SleepyTest()); + testSmellDetector.addDetectableSmell(new EagerTest()); + testSmellDetector.addDetectableSmell(new LazyTest()); + testSmellDetector.addDetectableSmell(new DuplicateAssert()); + testSmellDetector.addDetectableSmell(new UnknownTest()); + testSmellDetector.addDetectableSmell(new IgnoredTest()); + testSmellDetector.addDetectableSmell(new ResourceOptimism()); + testSmellDetector.addDetectableSmell(new MagicNumberTest()); + testSmellDetector.addDetectableSmell(new DependentTest()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + @Test + public void testSmellsFreeProject() throws IOException { + TestFile file = new TestFile( + "SmellsFreeProject", + "/Queue/src/test/java/br/ufmg/aserg/victorveloso/queue/QueueTest.java", + "/Queue/src/main/java/br/ufmg/aserg/victorveloso/queue/Queue.java" + ); + testSmellDetector.detectSmells(file); + + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + assertFalse(testSmell.hasSmell(), String.format("Detected smell named %s", testSmell.getSmellName())); + } + } + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java new file mode 100644 index 0000000..2c67176 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java @@ -0,0 +1,85 @@ +package edu.rit.se.testsmells.testsmell; + +import com.github.javaparser.ast.CompilationUnit; +import edu.rit.se.testsmells.testsmell.smell.AssertionRoulette; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class TestSmellDetectorTest { + private static final String TSStubName = "test"; + TestSmellDetector sut; + + @BeforeEach + void setUp() { + this.sut = TestSmellDetector.createTestSmellDetector(); + this.sut.addDetectableSmell(new TestSmellStub()); + } + + @Test + void getTestSmellNames() { + AssertionRoulette smell = new AssertionRoulette(); + this.sut.addDetectableSmell(smell); + + List testSmellNames = this.sut.getTestSmellNames(); + + assertTrue(testSmellNames.contains(TSStubName)); + assertTrue(testSmellNames.contains(smell.getSmellName())); + } + + @Test + void detectSmells() throws IOException { + TestFile tf = new TestFileStub("app", "", ""); + List smells; + + this.sut.detectSmells(tf); + smells = tf.getTestSmells(); + + assertEquals(smells.size(), 1); + assertFalse(smells.get(0).hasSmell()); + } + + private static class TestFileStub extends TestFile { + public TestFileStub(String app, String testFilePath, String productionFilePath) { + super(app, testFilePath, productionFilePath); + } + + @Override + protected void checkValidity(String testPath, String prodPath, String app) { + } + + @Override + public String getTestFileNameWithoutExtension() { + return ""; + } + + @Override + public String getProductionFileNameWithoutExtension() { + return ""; + } + } + + private static class TestSmellStub extends AbstractSmell { + + @Override + public String getSmellName() { + return TSStubName; + } + + @Override + public AbstractSmell recreate() { + return new TestSmellStub(); + } + + @Override + public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { + throw new FileNotFoundException("test"); + } + } +} \ No newline at end of file diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/AssertionRouletteIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/AssertionRouletteIntegrationTest.java new file mode 100644 index 0000000..4323d56 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/AssertionRouletteIntegrationTest.java @@ -0,0 +1,53 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class AssertionRouletteIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new AssertionRoulette()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + @Test + public void testAssertionRoulette() throws IOException { + TestFile file = new TestFile( + "AssertionRouletteProject", + "/AssertionRoulette/agit-integration-tests/src/main/java/com/madgag/agit/GitAsyncTaskTest.java", + "" + ); + + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new AssertionRoulette().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogicIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogicIntegrationTest.java new file mode 100644 index 0000000..5d4e101 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogicIntegrationTest.java @@ -0,0 +1,54 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class ConditionalTestLogicIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new ConditionalTestLogic()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + + @Test + public void testConditionalTestLogic() throws IOException { + + TestFile file = new TestFile( + "ConditionalTestLogicProject", + "/ConditionalTestLogic/src/test/java/com/dalthed/tucan/EventsScraperTest.java", + "" + ); + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new ConditionalTestLogic().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitializationIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitializationIntegrationTest.java new file mode 100644 index 0000000..eade4cf --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitializationIntegrationTest.java @@ -0,0 +1,54 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class ConstructorInitializationIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new ConstructorInitialization()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + + @Test + public void testConstructorInitialization() throws IOException { + + TestFile file = new TestFile( + "ConstructorInitializationProject", + "/ConstructorInitialization/src/test/java/org/briarproject/bramble/crypto/TagEncodingTest.java", + "" + ); + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new ConstructorInitialization().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/DefaultTestIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/DefaultTestIntegrationTest.java new file mode 100644 index 0000000..c7647d2 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/DefaultTestIntegrationTest.java @@ -0,0 +1,54 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class DefaultTestIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new DefaultTest()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + + @Test + public void testDefaultTest() throws IOException { + + TestFile file = new TestFile( + "DefaultTestProject", + "/DefaultTest/src/test/java/com/app/missednotificationsreminder/ExampleUnitTest.java", + "" + ); + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new DefaultTest().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssertIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssertIntegrationTest.java new file mode 100644 index 0000000..55842d5 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssertIntegrationTest.java @@ -0,0 +1,53 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class DuplicateAssertIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new DuplicateAssert()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + @Test + public void testDuplicateAssert() throws IOException { + TestFile file = new TestFile( + "DuplicateAssertProject", + "/DuplicateAssert/src/test/java/org/openbmap/utils/XmlSanitizerTest.java", + "" + ); + + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new DuplicateAssert().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/EagerTestIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/EagerTestIntegrationTest.java new file mode 100644 index 0000000..e9868d6 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/EagerTestIntegrationTest.java @@ -0,0 +1,54 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class EagerTestIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new EagerTest()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + + @Test + public void testEagerTest() throws IOException { + + TestFile file = new TestFile( + "EagerTestProject", + "/EagerTest/src/test/java/com/mendhak/gpslogger/loggers/nmea/NmeaSentenceTest.java", + "/EagerTest/src/main/java/com/mendhak/gpslogger/loggers/nmea/NmeaSentence.java" + ); + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new EagerTest().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/EmptyTestIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/EmptyTestIntegrationTest.java new file mode 100644 index 0000000..67aaa44 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/EmptyTestIntegrationTest.java @@ -0,0 +1,54 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class EmptyTestIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new EmptyTest()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + + @Test + public void testEmptyTest() throws IOException { + + TestFile file = new TestFile( + "EmptyTestProject", + "/EmptyTest/src/androidTest/java/com/actisec/clipcaster/parser/LastPassParserTest.java", + "" + ); + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new EmptyTest().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/ExceptionHandlingIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/ExceptionHandlingIntegrationTest.java new file mode 100644 index 0000000..35950d1 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/ExceptionHandlingIntegrationTest.java @@ -0,0 +1,54 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class ExceptionHandlingIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new ExceptionCatchingThrowing()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + + @Test + public void testExceptionHandling() throws IOException { + + TestFile file = new TestFile( + "ExceptionHandlingProject", + "/ExceptionHandling/src/androidTest/java/ch/hgdev/toposuite/test/calculation/AbrissTest.java", + "" + ); + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new ExceptionCatchingThrowing().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixtureIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixtureIntegrationTest.java new file mode 100644 index 0000000..309d174 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixtureIntegrationTest.java @@ -0,0 +1,55 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class GeneralFixtureIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new GeneralFixture()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + + @Test + public void testGeneralFixture() throws IOException { + + TestFile file = new TestFile( + "GeneralFixtureProject", + // Obtained from https://f-droid.org/repo/at.bitfire.cadroid_8_src.tar.gz + "/GeneralFixture/src/androidTest/java/at/bitfire/cadroid/test/CertificateInfoTest.java", + "" + ); + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new GeneralFixture().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTestIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTestIntegrationTest.java new file mode 100644 index 0000000..569af87 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTestIntegrationTest.java @@ -0,0 +1,55 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class IgnoredTestIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new IgnoredTest()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + + @Test + public void testIgnoredTest() throws IOException { + + TestFile file = new TestFile( + "IgnoredTestProject", + // Obtained from https://projects.owldevelopers.site/cryptocoin/bitcoinj/blob/9e4d25b2c2f3cce791853eeea4b07ddc2a2d3cf2/core/src/test/java/org/bitcoinj/core/PeerGroupTest.java + "/IgnoredTest/src/test/java/org/bitcoinj/core/PeerGroupTest.java", + "" + ); + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new IgnoredTest().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/LazyTestIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/LazyTestIntegrationTest.java new file mode 100644 index 0000000..0f3a3f4 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/LazyTestIntegrationTest.java @@ -0,0 +1,56 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class LazyTestIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new LazyTest()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + + @Test + public void testLazyTest() throws IOException { + + TestFile file = new TestFile( + "LazyTestProject", + // Obtained from https://github.com/jevgeniv/aRevelation/blob/950309c589d55b3fc8c879d548bae2224d558668/src/test/java/com/github/marmaladesky/tests/CryptographerTest.java + "/LazyTest/src/test/java/com/github/marmaladesky/tests/CryptographerTest.java", + // Obtained from https://github.com/jevgeniv/aRevelation/blob/950309c589d55b3fc8c879d548bae2224d558668/src/main/java/com/github/marmaladesky/Cryptographer.java + "/LazyTest/src/main/java/com/github/marmaladesky/Cryptographer.java" + ); + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new LazyTest().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTestIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTestIntegrationTest.java new file mode 100644 index 0000000..5d1e9ee --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTestIntegrationTest.java @@ -0,0 +1,53 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class MagicNumberTestIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new MagicNumberTest()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + @Test + public void testMagicNumberTest() throws IOException { + TestFile file = new TestFile( + "MagicNumberTestProject", + "/MagicNumberTest/src/test/java/com/luckycatlabs/sunrisesunset/calculator/SolarEventCalculatorTest.java", + "" + ); + + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new MagicNumberTest().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuestIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuestIntegrationTest.java new file mode 100644 index 0000000..5865cfe --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuestIntegrationTest.java @@ -0,0 +1,53 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class MysteryGuestIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new MysteryGuest()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + @Test + public void testMysteryGuest() throws IOException { + TestFile file = new TestFile( + "MysteryGuestProject", + "/MysteryGuest/src/androidTest/java/com/gmail/walles/johan/batterylogger/SystemStateTest.java", + "" + ); + + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new MysteryGuest().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertionIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertionIntegrationTest.java new file mode 100644 index 0000000..8cc9b91 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertionIntegrationTest.java @@ -0,0 +1,53 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class RedundantAssertionIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new RedundantAssertion()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + @Test + public void testRedundantAssertion() throws IOException { + TestFile file = new TestFile( + "RedundantAssertionProject", + "/RedundantAssertion/src/androidTest/java/com/litmus/worldscope/LoginActivityTest.java", + "" + ); + + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new RedundantAssertion().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/RedundantPrintIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/RedundantPrintIntegrationTest.java new file mode 100644 index 0000000..73bd5df --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/RedundantPrintIntegrationTest.java @@ -0,0 +1,53 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class RedundantPrintIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new PrintStatement()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + @Test + public void testRedundantPrint() throws IOException { + TestFile file = new TestFile( + "RedundantPrintProject", + "/RedundantPrint/src/test/java/org/hwyl/sexytopo/control/util/Space3DTransformerTest.java", + "" + ); + + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new PrintStatement().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimismIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimismIntegrationTest.java new file mode 100644 index 0000000..cb3fdcf --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimismIntegrationTest.java @@ -0,0 +1,54 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class ResourceOptimismIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new ResourceOptimism()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + + @Test + public void testResourceOptimism() throws IOException { + + TestFile file = new TestFile( + "ResourceOptimismProject", + "/ResourceOptimism/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LoggingTest.java", + "" + ); + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new ResourceOptimism().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEqualityIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEqualityIntegrationTest.java new file mode 100644 index 0000000..09c413c --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEqualityIntegrationTest.java @@ -0,0 +1,54 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class SensitiveEqualityIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new SensitiveEquality()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + + @Test + public void testSensitiveEquality() throws IOException { + + TestFile file = new TestFile( + "SensitiveEqualityProject", + "/SensitiveEquality/src/test/java/org/ethereum/util/RLPTest.java", + "" + ); + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new SensitiveEquality().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/SleepyTestIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/SleepyTestIntegrationTest.java new file mode 100644 index 0000000..b538b82 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/SleepyTestIntegrationTest.java @@ -0,0 +1,54 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class SleepyTestIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new SleepyTest()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + + @Test + public void testSleepyTest() throws IOException { + + TestFile file = new TestFile( + "SleepyTestProject", + "/SleepyTest/src/sk/baka/aedict/ResultActivityTest.java", + "" + ); + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new SleepyTest().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/smell/UnknownTestIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/UnknownTestIntegrationTest.java new file mode 100644 index 0000000..e3d70e1 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/UnknownTestIntegrationTest.java @@ -0,0 +1,54 @@ +package edu.rit.se.testsmells.testsmell.smell; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.IntegrationTest; +import edu.rit.se.testsmells.testsmell.TestFile; +import edu.rit.se.testsmells.testsmell.TestSmellDetector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@IntegrationTest +public class UnknownTestIntegrationTest { + + private TestSmellDetector testSmellDetector; + + @BeforeEach + void setUp() { + testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + testSmellDetector.addDetectableSmell(new UnknownTest()); + } + + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; + } + + + @Test + public void testUnknownTest() throws IOException { + + TestFile file = new TestFile( + "UnknownTestProject", + "/UnknownTest/src/test/java/net/cyclestreets/api/client/RetrofitApiClientIntegrationTest.java", + "" + ); + testSmellDetector.detectSmells(file); + boolean expectedSmellDetected = false; + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + if (testSmell.getSmellName().equals(new UnknownTest().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } + } + } + assertTrue(expectedSmellDetected); + + } +}