From 29d6a9bae50f76d755d0e2ecd23f065527ad013a Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Wed, 26 Aug 2020 04:46:27 -0300 Subject: [PATCH 01/42] AbstractSmell became interface, getHasSmell and getHasProductionFile renamed to hasSmell and hasProductionFile, reformatted code, pulled up similar members between TestClass and TestMethod, removed commented code, redundant comments and unused methods and variables, moved member initializeSmells to Main, extracted parseIntoCompilationUnit from duplication on detectSmells and reorganized for-loop on detectSmells --- class_diagram.uml | 576 ++++-------------- .../{ => edu/rit/se/testsmells}/Main.java | 59 +- .../testsmells/testsmell/AbstractSmell.java | 16 + .../testsmells}/testsmell/ResultsWriter.java | 25 +- .../testsmells/testsmell/SmellyElement.java} | 20 +- .../se/testsmells/testsmell/TestClass.java | 9 + .../se/testsmells}/testsmell/TestFile.java | 47 +- .../se/testsmells/testsmell/TestMethod.java | 9 + .../testsmell/TestSmellDetector.java | 78 +++ .../rit/se/testsmells}/testsmell/Util.java | 20 +- .../testsmell/smell/AssertionRoulette.java | 17 +- .../testsmell/smell/ConditionalTestLogic.java | 17 +- .../smell/ConstructorInitialization.java | 15 +- .../testsmell/smell/DefaultTest.java | 15 +- .../testsmell/smell/DependentTest.java | 58 +- .../testsmell/smell/DuplicateAssert.java | 22 +- .../testsmell/smell/EagerTest.java | 41 +- .../testsmell/smell/EmptyTest.java | 17 +- .../smell/ExceptionCatchingThrowing.java | 17 +- .../testsmell/smell/GeneralFixture.java | 17 +- .../testsmell/smell/IgnoredTest.java | 17 +- .../testsmells}/testsmell/smell/LazyTest.java | 18 +- .../testsmell/smell/MagicNumberTest.java | 17 +- .../testsmell/smell/MysteryGuest.java | 17 +- .../testsmell/smell/PrintStatement.java | 17 +- .../testsmell/smell/RedundantAssertion.java | 17 +- .../testsmell/smell/ResourceOptimism.java | 22 +- .../testsmell/smell/SensitiveEquality.java | 17 +- .../testsmell/smell/SleepyTest.java | 17 +- .../testsmell/smell/UnknownTest.java | 17 +- .../testsmell/smell/VerboseTest.java | 17 +- src/main/java/testsmell/AbstractSmell.java | 16 - src/main/java/testsmell/SmellyElement.java | 11 - src/main/java/testsmell/TestMethod.java | 39 -- .../java/testsmell/TestSmellDetector.java | 108 ---- .../testsmells}/testsmell/TestFileTest.java | 4 +- 36 files changed, 527 insertions(+), 939 deletions(-) rename src/main/java/{ => edu/rit/se/testsmells}/Main.java (60%) create mode 100644 src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/ResultsWriter.java (74%) rename src/main/java/{testsmell/TestClass.java => edu/rit/se/testsmells/testsmell/SmellyElement.java} (63%) create mode 100644 src/main/java/edu/rit/se/testsmells/testsmell/TestClass.java rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/TestFile.java (71%) create mode 100644 src/main/java/edu/rit/se/testsmells/testsmell/TestMethod.java create mode 100644 src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/Util.java (83%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/AssertionRoulette.java (92%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/ConditionalTestLogic.java (91%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/ConstructorInitialization.java (87%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/DefaultTest.java (84%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/DependentTest.java (80%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/DuplicateAssert.java (90%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/EagerTest.java (85%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/EmptyTest.java (85%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/ExceptionCatchingThrowing.java (88%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/GeneralFixture.java (93%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/IgnoredTest.java (88%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/LazyTest.java (95%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/MagicNumberTest.java (91%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/MysteryGuest.java (93%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/PrintStatement.java (90%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/RedundantAssertion.java (93%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/ResourceOptimism.java (89%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/SensitiveEquality.java (89%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/SleepyTest.java (88%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/UnknownTest.java (90%) rename src/main/java/{ => edu/rit/se/testsmells}/testsmell/smell/VerboseTest.java (86%) delete mode 100644 src/main/java/testsmell/AbstractSmell.java delete mode 100644 src/main/java/testsmell/SmellyElement.java delete mode 100644 src/main/java/testsmell/TestMethod.java delete mode 100644 src/main/java/testsmell/TestSmellDetector.java rename src/test/java/{ => edu/rit/se/testsmells}/testsmell/TestFileTest.java (96%) 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/src/main/java/Main.java b/src/main/java/edu/rit/se/testsmells/Main.java similarity index 60% rename from src/main/java/Main.java rename to src/main/java/edu/rit/se/testsmells/Main.java index 94f6786..a767659 100644 --- a/src/main/java/Main.java +++ b/src/main/java/edu/rit/se/testsmells/Main.java @@ -1,9 +1,10 @@ -import testsmell.AbstractSmell; -import testsmell.ResultsWriter; -import testsmell.TestFile; -import testsmell.TestSmellDetector; -import testsmell.smell.AssertionRoulette; -import testsmell.smell.EagerTest; +package edu.rit.se.testsmells; + +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.ResultsWriter; +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; @@ -21,15 +22,17 @@ public static void main(String[] args) throws IOException { System.out.println("Please provide the file containing the paths to the collection of test files"); return; } - if(!args[0].isEmpty()){ + if (!args[0].isEmpty()) { File inputFile = new File(args[0]); - if(!inputFile.exists() || inputFile.isDirectory()) { + 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(); + TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); + + initializeSmells(testSmellDetector); /* Read the input file and build the TestFile objects @@ -45,10 +48,9 @@ public static void main(String[] args) throws IOException { lineItem = str.split(","); //check if the test file has an associated production file - if(lineItem.length ==2){ + if (lineItem.length == 2) { testFile = new TestFile(lineItem[0], lineItem[1], ""); - } - else{ + } else { testFile = new TestFile(lineItem[0], lineItem[1], lineItem[2]); } @@ -80,8 +82,8 @@ public static void main(String[] args) throws IOException { Date date; for (TestFile file : testFiles) { date = new Date(); - System.out.println(dateFormat.format(date) + " Processing: "+file.getTestFilePath()); - System.out.println("Processing: "+file.getTestFilePath()); + System.out.println(dateFormat.format(date) + " Processing: " + file.getTestFilePath()); + System.out.println("Processing: " + file.getTestFilePath()); //detect smells tempFile = testSmellDetector.detectSmells(file); @@ -96,9 +98,8 @@ public static void main(String[] args) throws IOException { columnValues.add(file.getRelativeProductionFilePath()); for (AbstractSmell smell : tempFile.getTestSmells()) { try { - columnValues.add(String.valueOf(smell.getHasSmell())); - } - catch (NullPointerException e){ + columnValues.add(String.valueOf(smell.hasSmell())); + } catch (NullPointerException e) { columnValues.add(""); } } @@ -108,5 +109,29 @@ public static void main(String[] args) throws IOException { System.out.println("end"); } + private static void initializeSmells(TestSmellDetector testSmellDetector) { + testSmellDetector.addSmell(new AssertionRoulette()); + testSmellDetector.addSmell(new ConditionalTestLogic()); + testSmellDetector.addSmell(new ConstructorInitialization()); + testSmellDetector.addSmell(new DefaultTest()); + testSmellDetector.addSmell(new EmptyTest()); + testSmellDetector.addSmell(new ExceptionCatchingThrowing()); + testSmellDetector.addSmell(new GeneralFixture()); + testSmellDetector.addSmell(new MysteryGuest()); + testSmellDetector.addSmell(new PrintStatement()); + testSmellDetector.addSmell(new RedundantAssertion()); + testSmellDetector.addSmell(new SensitiveEquality()); + testSmellDetector.addSmell(new VerboseTest()); + testSmellDetector.addSmell(new SleepyTest()); + testSmellDetector.addSmell(new EagerTest()); + testSmellDetector.addSmell(new LazyTest()); + testSmellDetector.addSmell(new DuplicateAssert()); + testSmellDetector.addSmell(new UnknownTest()); + testSmellDetector.addSmell(new IgnoredTest()); + testSmellDetector.addSmell(new ResourceOptimism()); + testSmellDetector.addSmell(new MagicNumberTest()); + testSmellDetector.addSmell(new DependentTest()); + } + } 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..abbc755 --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java @@ -0,0 +1,16 @@ +package edu.rit.se.testsmells.testsmell; + +import com.github.javaparser.ast.CompilationUnit; + +import java.io.FileNotFoundException; +import java.util.List; + +public interface AbstractSmell { + String getSmellName(); + + boolean hasSmell(); + + void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException; + + List getSmellyElements(); +} diff --git a/src/main/java/testsmell/ResultsWriter.java b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java similarity index 74% rename from src/main/java/testsmell/ResultsWriter.java rename to src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java index e980397..0f26dd2 100644 --- a/src/main/java/testsmell/ResultsWriter.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java @@ -1,4 +1,4 @@ -package testsmell; +package edu.rit.se.testsmells.testsmell; import java.io.FileWriter; import java.io.IOException; @@ -16,16 +16,18 @@ public class ResultsWriter { /** * 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); + 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 */ @@ -35,6 +37,7 @@ public static ResultsWriter createResultsWriter() throws IOException { /** * Writes column names into the CSV file + * * @param columnNames the column names * @throws IOException */ @@ -44,6 +47,7 @@ public void writeColumnName(List columnNames) throws IOException { /** * Writes column values into the CSV file + * * @param columnValues the column values * @throws IOException */ @@ -53,19 +57,18 @@ public void writeLine(List columnValues) throws IOException { /** * 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); + private void writeOutput(List dataValues) throws IOException { + writer = new FileWriter(outputFile, true); - for (int i=0; i data; + private boolean hasSmell; - public TestClass(String className) { - this.className = className; + public SmellyElement(String name) { data = new HashMap<>(); + this.name = name; } public void setHasSmell(boolean hasSmell) { @@ -22,17 +21,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..b23c5f1 --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestClass.java @@ -0,0 +1,9 @@ +package edu.rit.se.testsmells.testsmell; + +public class TestClass extends SmellyElement { + + public TestClass(String name) { + super(name); + } + +} diff --git a/src/main/java/testsmell/TestFile.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java similarity index 71% rename from src/main/java/testsmell/TestFile.java rename to src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java index 333ba84..70cd9ba 100644 --- a/src/main/java/testsmell/TestFile.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java @@ -1,4 +1,4 @@ -package testsmell; +package edu.rit.se.testsmells.testsmell; import org.apache.commons.lang3.StringUtils; @@ -26,7 +26,7 @@ public List getTestSmells() { return testSmells; } - public boolean getHasProductionFile() { + public boolean hasProductionFile() { return ((productionFilePath != null && !productionFilePath.isEmpty())); } @@ -46,62 +46,57 @@ public void addSmell(AbstractSmell smell) { * Returns the "N.I.Y", Not Implemented Yet string * todo: not implemented in any way yet */ - public String getTagName(){ + public String getTagName() { return "N.I.Y"; } - public String getTestFileName(){ + public String getTestFileName() { int lastIndex = testFilePath.lastIndexOf(File.separator); - return testFilePath.substring(lastIndex+1); + return testFilePath.substring(lastIndex + 1); } - public String getTestFileNameWithoutExtension(){ + public String getTestFileNameWithoutExtension() { int lastIndex = getTestFileName().lastIndexOf("."); - return getTestFileName().substring(0,lastIndex); + return getTestFileName().substring(0, lastIndex); } - public String getProductionFileNameWithoutExtension(){ + public String getProductionFileNameWithoutExtension() { int lastIndex = getProductionFileName().lastIndexOf("."); - if(lastIndex==-1) - return ""; - return getProductionFileName().substring(0,lastIndex); + if (lastIndex == -1) return ""; + return getProductionFileName().substring(0, lastIndex); } - public String getProductionFileName(){ + public String getProductionFileName() { int lastIndex = productionFilePath.lastIndexOf(File.separator); - if(lastIndex==-1) - return ""; - return productionFilePath.substring(lastIndex+1); + 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 ""; + 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 * + * @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 ""; + 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/edu/rit/se/testsmells/testsmell/TestMethod.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestMethod.java new file mode 100644 index 0000000..71a4a7e --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestMethod.java @@ -0,0 +1,9 @@ +package edu.rit.se.testsmells.testsmell; + +public class TestMethod extends SmellyElement { + + public TestMethod(String name) { + super(name); + } + +} 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..5219574 --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java @@ -0,0 +1,78 @@ +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.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() { + testSmells = new ArrayList<>(); + } + + public void addSmell(AbstractSmell smell) { + testSmells.add(smell); + } + + /** + * 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 = parseIntoCompilationUnit(testFile.getTestFilePath()); + + CompilationUnit productionFileCompilationUnit = parseIntoCompilationUnit(testFile.getProductionFilePath()); + + for (AbstractSmell smell : testSmells) { + try { + smell.runAnalysis(testFileCompilationUnit, productionFileCompilationUnit, testFile.getTestFileNameWithoutExtension(), testFile.getProductionFileNameWithoutExtension()); + testFile.addSmell(smell); + } finally { + testFile.addSmell(null); + } + } + + return testFile; + + } + + private CompilationUnit parseIntoCompilationUnit(String testFilePath) throws FileNotFoundException { + if (StringUtils.isEmpty(testFilePath)) { + return null; + } + FileInputStream testFileInputStream; + testFileInputStream = new FileInputStream(testFilePath); + return JavaParser.parse(testFileInputStream); + } + + +} diff --git a/src/main/java/testsmell/Util.java b/src/main/java/edu/rit/se/testsmells/testsmell/Util.java similarity index 83% rename from src/main/java/testsmell/Util.java rename to src/main/java/edu/rit/se/testsmells/testsmell/Util.java index b1830c7..1e5f411 100644 --- a/src/main/java/testsmell/Util.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/Util.java @@ -1,4 +1,4 @@ -package testsmell; +package edu.rit.se.testsmells.testsmell; import com.github.javaparser.ast.Modifier; import com.github.javaparser.ast.body.MethodDeclaration; @@ -37,21 +37,21 @@ public static boolean isValidSetupMethod(MethodDeclaration n) { return valid; } - public static boolean isInt(String s) - { - try - { int i = Integer.parseInt(s); return true; } - - catch(NumberFormatException er) - { return false; } + public static boolean isInt(String s) { + try { + Integer.parseInt(s); + return true; + } catch (NumberFormatException er) { + return false; + } } public static boolean isNumber(String str) { try { - double v = Double.parseDouble(str); + Double.parseDouble(str); return true; } catch (NumberFormatException nfe) { + return false; } - return false; } } diff --git a/src/main/java/testsmell/smell/AssertionRoulette.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java similarity index 92% rename from src/main/java/testsmell/smell/AssertionRoulette.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java index ff196db..17619d9 100644 --- a/src/main/java/testsmell/smell/AssertionRoulette.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java @@ -1,13 +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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -18,7 +18,7 @@ * If one of the assertions fails, you do not know which one it is. * A. van Deursen, L. Moonen, A. Bergh, G. Kok, “Refactoring Test Code”, Technical Report, CWI, 2001. */ -public class AssertionRoulette extends AbstractSmell { +public class AssertionRoulette implements AbstractSmell { private List smellyElementList; private int assertionsCount = 0; @@ -38,9 +38,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** diff --git a/src/main/java/testsmell/smell/ConditionalTestLogic.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java similarity index 91% rename from src/main/java/testsmell/smell/ConditionalTestLogic.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java index 1279ac5..24af25a 100644 --- a/src/main/java/testsmell/smell/ConditionalTestLogic.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java @@ -1,14 +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.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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -17,7 +17,7 @@ /* This class check a test method for the existence of loops and conditional statements in the methods body */ -public class ConditionalTestLogic extends AbstractSmell { +public class ConditionalTestLogic implements AbstractSmell { private List smellyElementList; public ConditionalTestLogic() { @@ -35,9 +35,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** diff --git a/src/main/java/testsmell/smell/ConstructorInitialization.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java similarity index 87% rename from src/main/java/testsmell/smell/ConstructorInitialization.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java index c964fc9..1e59d3b 100644 --- a/src/main/java/testsmell/smell/ConstructorInitialization.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java @@ -1,13 +1,13 @@ -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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestClass; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -18,7 +18,7 @@ This class checks if the code file contains a Constructor. Ideally, the test suite should not have a constructor. Initialization of fields should be in the setUP() method If this code detects the existence of a constructor, it sets the class as smelly */ -public class ConstructorInitialization extends AbstractSmell { +public class ConstructorInitialization implements AbstractSmell { private List smellyElementList; private String testFileName; @@ -38,9 +38,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** diff --git a/src/main/java/testsmell/smell/DefaultTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DefaultTest.java similarity index 84% rename from src/main/java/testsmell/smell/DefaultTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/DefaultTest.java index e630946..e7008c9 100644 --- a/src/main/java/testsmell/smell/DefaultTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DefaultTest.java @@ -1,11 +1,11 @@ -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.visitor.VoidVisitorAdapter; -import testsmell.AbstractSmell; -import testsmell.SmellyElement; -import testsmell.TestClass; +import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestClass; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -15,7 +15,7 @@ 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 { +public class DefaultTest implements AbstractSmell { private List smellyElementList; @@ -34,9 +34,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } @Override diff --git a/src/main/java/testsmell/smell/DependentTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java similarity index 80% rename from src/main/java/testsmell/smell/DependentTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java index 38fb2bd..2e49ed9 100644 --- a/src/main/java/testsmell/smell/DependentTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java @@ -1,18 +1,18 @@ -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.SmellyElement; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; -public class DependentTest extends AbstractSmell { +public class DependentTest implements AbstractSmell { private List smellyElementList; private List testMethods; @@ -34,9 +34,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** @@ -50,18 +49,9 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit 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())); + smellyElementList.add(new edu.rit.se.testsmells.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())); - } - } - }*/ } /** @@ -101,6 +91,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 +106,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 +124,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 90% rename from src/main/java/testsmell/smell/DuplicateAssert.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java index fcc6037..c621348 100644 --- a/src/main/java/testsmell/smell/DuplicateAssert.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java @@ -1,18 +1,21 @@ -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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; 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 { +public class DuplicateAssert implements AbstractSmell { private List smellyElementList; @@ -31,9 +34,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** diff --git a/src/main/java/testsmell/smell/EagerTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java similarity index 85% rename from src/main/java/testsmell/smell/EagerTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java index 7412616..892284a 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,16 +8,18 @@ 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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; import java.util.Objects; -public class EagerTest extends AbstractSmell { +public class EagerTest implements AbstractSmell { private static final String TEST_FILE = "Test"; private static final String PRODUCTION_FILE = "Production"; @@ -43,8 +45,8 @@ public String getSmellName() { * Returns true if any of the elements has a smell */ @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** @@ -164,8 +166,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 +178,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 +193,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 +204,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 +212,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 85% rename from src/main/java/testsmell/smell/EmptyTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java index 4bc18ec..3129d3d 100644 --- a/src/main/java/testsmell/smell/EmptyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java @@ -1,12 +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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -16,7 +16,7 @@ * This class checks if a test method is empty (i.e. the method does not contain statements in its body) * If the the number of statements in the body is 0, then the method is smelly */ -public class EmptyTest extends AbstractSmell { +public class EmptyTest implements AbstractSmell { private List smellyElementList; @@ -35,9 +35,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** diff --git a/src/main/java/testsmell/smell/ExceptionCatchingThrowing.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java similarity index 88% rename from src/main/java/testsmell/smell/ExceptionCatchingThrowing.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java index 50cc5e0..6363bb3 100644 --- a/src/main/java/testsmell/smell/ExceptionCatchingThrowing.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java @@ -1,14 +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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -18,7 +18,7 @@ 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 If this code detects the existence of a catch block or a throw statement in the methods body, the method is marked as smelly */ -public class ExceptionCatchingThrowing extends AbstractSmell { +public class ExceptionCatchingThrowing implements AbstractSmell { private List smellyElementList; @@ -37,9 +37,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** diff --git a/src/main/java/testsmell/smell/GeneralFixture.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java similarity index 93% rename from src/main/java/testsmell/smell/GeneralFixture.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java index 9d5ddf5..6e48bfc 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,15 +11,15 @@ 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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.*; -public class GeneralFixture extends AbstractSmell { +public class GeneralFixture implements AbstractSmell { private List smellyElementList; List methodList; @@ -45,9 +45,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } @Override diff --git a/src/main/java/testsmell/smell/IgnoredTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java similarity index 88% rename from src/main/java/testsmell/smell/IgnoredTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java index 275bdbd..f90d59d 100644 --- a/src/main/java/testsmell/smell/IgnoredTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java @@ -1,20 +1,20 @@ -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.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.SmellyElement; +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 { +public class IgnoredTest implements AbstractSmell { private List smellyElementList; @@ -33,9 +33,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** diff --git a/src/main/java/testsmell/smell/LazyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java similarity index 95% rename from src/main/java/testsmell/smell/LazyTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java index 55efa39..39b92ea 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,11 @@ 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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -21,7 +20,7 @@ import java.util.Objects; import java.util.stream.Collectors; -public class LazyTest extends AbstractSmell { +public class LazyTest implements AbstractSmell { private static final String TEST_FILE = "Test"; private static final String PRODUCTION_FILE = "Production"; private String productionClassName; @@ -46,9 +45,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** diff --git a/src/main/java/testsmell/smell/MagicNumberTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java similarity index 91% rename from src/main/java/testsmell/smell/MagicNumberTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java index 4643795..7a93572 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,16 +6,16 @@ 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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; -public class MagicNumberTest extends AbstractSmell { +public class MagicNumberTest implements AbstractSmell { private List smellyElementList; @@ -34,9 +34,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** diff --git a/src/main/java/testsmell/smell/MysteryGuest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java similarity index 93% rename from src/main/java/testsmell/smell/MysteryGuest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java index 6aa1dc8..24993c8 100644 --- a/src/main/java/testsmell/smell/MysteryGuest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java @@ -1,14 +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.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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -22,7 +22,7 @@ * Chances for this increase when more tests use the same resource. * A. van Deursen, L. Moonen, A. Bergh, G. Kok, “Refactoring Test Code”, Technical Report, CWI, 2001. */ -public class MysteryGuest extends AbstractSmell { +public class MysteryGuest implements AbstractSmell { private List smellyElementList; @@ -41,9 +41,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** diff --git a/src/main/java/testsmell/smell/PrintStatement.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java similarity index 90% rename from src/main/java/testsmell/smell/PrintStatement.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java index 3bcf3d0..3c94c48 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,10 +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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -19,7 +19,7 @@ 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. This code checks the body of each test method if System.out. print(), println(), printf() and write() methods are called */ -public class PrintStatement extends AbstractSmell { +public class PrintStatement implements AbstractSmell { private List smellyElementList; @@ -38,9 +38,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** diff --git a/src/main/java/testsmell/smell/RedundantAssertion.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java similarity index 93% rename from src/main/java/testsmell/smell/RedundantAssertion.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java index 47431d5..f7eefce 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,10 +6,10 @@ 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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -18,7 +18,7 @@ /* 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 { +public class RedundantAssertion implements AbstractSmell { private List smellyElementList; @@ -37,9 +37,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** diff --git a/src/main/java/testsmell/smell/ResourceOptimism.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java similarity index 89% rename from src/main/java/testsmell/smell/ResourceOptimism.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java index 26e5efb..a6d7abb 100644 --- a/src/main/java/testsmell/smell/ResourceOptimism.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java @@ -1,21 +1,24 @@ -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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; -public class ResourceOptimism extends AbstractSmell { +public class ResourceOptimism implements AbstractSmell { private List smellyElementList; @@ -34,9 +37,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** diff --git a/src/main/java/testsmell/smell/SensitiveEquality.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java similarity index 89% rename from src/main/java/testsmell/smell/SensitiveEquality.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java index 726df94..41fd68c 100644 --- a/src/main/java/testsmell/smell/SensitiveEquality.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java @@ -1,20 +1,20 @@ -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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; -public class SensitiveEquality extends AbstractSmell { +public class SensitiveEquality implements AbstractSmell { private List smellyElementList; @@ -33,9 +33,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** diff --git a/src/main/java/testsmell/smell/SleepyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java similarity index 88% rename from src/main/java/testsmell/smell/SleepyTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java index bd397b3..4f7b38e 100644 --- a/src/main/java/testsmell/smell/SleepyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java @@ -1,14 +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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -18,7 +18,7 @@ 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 This code marks a method as smelly if the method body calls Thread.sleep() */ -public class SleepyTest extends AbstractSmell { +public class SleepyTest implements AbstractSmell { private List smellyElementList; @@ -37,9 +37,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** diff --git a/src/main/java/testsmell/smell/UnknownTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java similarity index 90% rename from src/main/java/testsmell/smell/UnknownTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java index 4c3d43c..5c67f53 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,17 +7,17 @@ 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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; import java.util.Optional; -public class UnknownTest extends AbstractSmell { +public class UnknownTest implements AbstractSmell { private List smellyElementList; @@ -36,9 +36,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** diff --git a/src/main/java/testsmell/smell/VerboseTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java similarity index 86% rename from src/main/java/testsmell/smell/VerboseTest.java rename to src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java index 28346a9..fec86ba 100644 --- a/src/main/java/testsmell/smell/VerboseTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java @@ -1,12 +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.SmellyElement; +import edu.rit.se.testsmells.testsmell.TestMethod; +import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -15,7 +15,7 @@ /* If a test methods contains a statements that exceeds a certain threshold, the method is marked as smelly */ -public class VerboseTest extends AbstractSmell { +public class VerboseTest implements AbstractSmell { private List smellyElementList; @@ -34,9 +34,8 @@ public String getSmellName() { /** * Returns true if any of the elements has a smell */ - @Override - public boolean getHasSmell() { - return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } /** 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/SmellyElement.java b/src/main/java/testsmell/SmellyElement.java deleted file mode 100644 index e56ebe6..0000000 --- a/src/main/java/testsmell/SmellyElement.java +++ /dev/null @@ -1,11 +0,0 @@ -package testsmell; - -import java.util.Map; - -public abstract class SmellyElement { - public abstract String getElementName(); - - public abstract boolean getHasSmell(); - - public abstract Map getData(); -} 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/test/java/testsmell/TestFileTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestFileTest.java similarity index 96% rename from src/test/java/testsmell/TestFileTest.java rename to src/test/java/edu/rit/se/testsmells/testsmell/TestFileTest.java index 54e0d52..df05f0c 100644 --- a/src/test/java/testsmell/TestFileTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestFileTest.java @@ -1,11 +1,11 @@ -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 static org.junit.jupiter.api.Assertions.assertEquals; class TestFileTest { From b33956d890b60c3069ff96899bfe2a0785a9d936 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Wed, 26 Aug 2020 06:54:01 -0300 Subject: [PATCH 02/42] Moved Util members to AbstractSmell (making it abstract class again), fixed some warnings from GeneralFixture.java --- .../testsmells/testsmell/AbstractSmell.java | 54 ++++++++++++++++-- .../edu/rit/se/testsmells/testsmell/Util.java | 57 ------------------- .../testsmell/smell/AssertionRoulette.java | 5 +- .../testsmell/smell/ConditionalTestLogic.java | 5 +- .../smell/ConstructorInitialization.java | 2 +- .../testsmell/smell/DefaultTest.java | 2 +- .../testsmell/smell/DependentTest.java | 5 +- .../testsmell/smell/DuplicateAssert.java | 5 +- .../testsmells/testsmell/smell/EagerTest.java | 5 +- .../testsmells/testsmell/smell/EmptyTest.java | 5 +- .../smell/ExceptionCatchingThrowing.java | 5 +- .../testsmell/smell/GeneralFixture.java | 29 ++++------ .../testsmell/smell/IgnoredTest.java | 2 +- .../testsmells/testsmell/smell/LazyTest.java | 5 +- .../testsmell/smell/MagicNumberTest.java | 29 +++++----- .../testsmell/smell/MysteryGuest.java | 5 +- .../testsmell/smell/PrintStatement.java | 5 +- .../testsmell/smell/RedundantAssertion.java | 5 +- .../testsmell/smell/ResourceOptimism.java | 7 +-- .../testsmell/smell/SensitiveEquality.java | 5 +- .../testsmell/smell/SleepyTest.java | 5 +- .../testsmell/smell/UnknownTest.java | 5 +- .../testsmell/smell/VerboseTest.java | 5 +- 23 files changed, 111 insertions(+), 146 deletions(-) delete mode 100644 src/main/java/edu/rit/se/testsmells/testsmell/Util.java diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java index abbc755..e5f4833 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java @@ -1,16 +1,60 @@ package edu.rit.se.testsmells.testsmell; import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.Modifier; +import com.github.javaparser.ast.body.MethodDeclaration; import java.io.FileNotFoundException; import java.util.List; -public interface AbstractSmell { - String getSmellName(); +public abstract class AbstractSmell { - boolean hasSmell(); + public abstract String getSmellName(); - void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException; + public abstract boolean hasSmell(); - List getSmellyElements(); + public abstract void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException; + + public abstract List getSmellyElements(); + + protected 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; + } + + protected boolean isNumber(String str) { + try { + Double.parseDouble(str); + return true; + } catch (NumberFormatException nfe) { + return false; + } + } + + protected 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; + } } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/Util.java b/src/main/java/edu/rit/se/testsmells/testsmell/Util.java deleted file mode 100644 index 1e5f411..0000000 --- a/src/main/java/edu/rit/se/testsmells/testsmell/Util.java +++ /dev/null @@ -1,57 +0,0 @@ -package edu.rit.se.testsmells.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 { - Integer.parseInt(s); - return true; - } catch (NumberFormatException er) { - return false; - } - } - - public static boolean isNumber(String str) { - try { - Double.parseDouble(str); - return true; - } catch (NumberFormatException nfe) { - return false; - } - } -} diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java index 17619d9..f326a94 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java @@ -7,7 +7,6 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -18,7 +17,7 @@ * If one of the assertions fails, you do not know which one it is. * A. van Deursen, L. Moonen, A. Bergh, G. Kok, “Refactoring Test Code”, Technical Report, CWI, 2001. */ -public class AssertionRoulette implements AbstractSmell { +public class AssertionRoulette extends AbstractSmell { private List smellyElementList; private int assertionsCount = 0; @@ -76,7 +75,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; testMethod = new TestMethod(n.getNameAsString()); testMethod.setHasSmell(false); //default value is false (i.e. no smell) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java index 24af25a..b47eb8a 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java @@ -8,7 +8,6 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -17,7 +16,7 @@ /* This class check a test method for the existence of loops and conditional statements in the methods body */ -public class ConditionalTestLogic implements AbstractSmell { +public class ConditionalTestLogic extends AbstractSmell { private List smellyElementList; public ConditionalTestLogic() { @@ -66,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; testMethod = new TestMethod(n.getNameAsString()); testMethod.setHasSmell(false); //default value is false (i.e. no smell) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java index 1e59d3b..8dedad5 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java @@ -18,7 +18,7 @@ This class checks if the code file contains a Constructor. Ideally, the test suite should not have a constructor. Initialization of fields should be in the setUP() method If this code detects the existence of a constructor, it sets the class as smelly */ -public class ConstructorInitialization implements AbstractSmell { +public class ConstructorInitialization extends AbstractSmell { private List smellyElementList; private String testFileName; 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 index e7008c9..77ac41a 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DefaultTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DefaultTest.java @@ -15,7 +15,7 @@ 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 implements AbstractSmell { +public class DefaultTest extends AbstractSmell { private List smellyElementList; diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java index 2e49ed9..62458bc 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java @@ -6,13 +6,12 @@ import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; -public class DependentTest implements AbstractSmell { +public class DependentTest extends AbstractSmell { private List smellyElementList; private List testMethods; @@ -69,7 +68,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<>(); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java index c621348..777c703 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java @@ -7,7 +7,6 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -15,7 +14,7 @@ import java.util.List; import java.util.Set; -public class DuplicateAssert implements AbstractSmell { +public class DuplicateAssert extends AbstractSmell { private List smellyElementList; @@ -66,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; testMethod = new TestMethod(n.getNameAsString()); testMethod.setHasSmell(false); //default value is false (i.e. no smell) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java index 892284a..dcfc97f 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java @@ -12,14 +12,13 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; import java.util.Objects; -public class EagerTest implements AbstractSmell { +public class EagerTest extends AbstractSmell { private static final String TEST_FILE = "Test"; private static final String PRODUCTION_FILE = "Production"; @@ -119,7 +118,7 @@ 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.setHasSmell(false); //default value is false (i.e. no smell) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java index 3129d3d..676cd5b 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java @@ -6,7 +6,6 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -16,7 +15,7 @@ * This class checks if a test method is empty (i.e. the method does not contain statements in its body) * If the the number of statements in the body is 0, then the method is smelly */ -public class EmptyTest implements AbstractSmell { +public class EmptyTest extends AbstractSmell { private List smellyElementList; @@ -68,7 +67,7 @@ private class ClassVisitor extends VoidVisitorAdapter { */ @Override public void visit(MethodDeclaration n, Void arg) { - if (Util.isValidTestMethod(n)) { + if (isValidTestMethod(n)) { testMethod = new TestMethod(n.getNameAsString()); testMethod.setHasSmell(false); //default value is false (i.e. no smell) //method should not be abstract diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java index 6363bb3..e15fa1f 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java @@ -8,7 +8,6 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -18,7 +17,7 @@ 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 If this code detects the existence of a catch block or a throw statement in the methods body, the method is marked as smelly */ -public class ExceptionCatchingThrowing implements AbstractSmell { +public class ExceptionCatchingThrowing extends AbstractSmell { private List smellyElementList; @@ -68,7 +67,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; testMethod = new TestMethod(n.getNameAsString()); testMethod.setHasSmell(false); //default value is false (i.e. no smell) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java index 6e48bfc..83ee3bb 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java @@ -14,12 +14,11 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.*; -public class GeneralFixture implements AbstractSmell { +public class GeneralFixture extends AbstractSmell { private List smellyElementList; List methodList; @@ -95,25 +94,24 @@ public List getSmellyElements() { 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; @@ -122,8 +120,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); } } } @@ -131,7 +129,7 @@ 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 @@ -141,7 +139,7 @@ public void visit(MethodDeclaration n, Void arg) { testMethod.setHasSmell(fixtureCount.size() != setupFields.size()); smellyElementList.add(testMethod); - fixtureCount = new HashSet();; + fixtureCount = new HashSet<>(); currentMethod = null; } } @@ -151,10 +149,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/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java index f90d59d..361e6b7 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java @@ -14,7 +14,7 @@ import java.util.ArrayList; import java.util.List; -public class IgnoredTest implements AbstractSmell { +public class IgnoredTest extends AbstractSmell { private List smellyElementList; diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java index 39b92ea..cf96f70 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java @@ -12,7 +12,6 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -20,7 +19,7 @@ import java.util.Objects; import java.util.stream.Collectors; -public class LazyTest implements AbstractSmell { +public class LazyTest extends AbstractSmell { private static final String TEST_FILE = "Test"; private static final String PRODUCTION_FILE = "Production"; private String productionClassName; @@ -141,7 +140,7 @@ 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.setHasSmell(false); //default value is false (i.e. no smell) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java index 7a93572..fe873be 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java @@ -9,13 +9,12 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; -public class MagicNumberTest implements AbstractSmell { +public class MagicNumberTest extends AbstractSmell { private List smellyElementList; @@ -64,7 +63,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; testMethod = new TestMethod(n.getNameAsString()); testMethod.setHasSmell(false); //default value is false (i.e. no smell) @@ -97,21 +96,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/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java index 24993c8..d763a5d 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java @@ -8,7 +8,6 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -22,7 +21,7 @@ * Chances for this increase when more tests use the same resource. * A. van Deursen, L. Moonen, A. Bergh, G. Kok, “Refactoring Test Code”, Technical Report, CWI, 2001. */ -public class MysteryGuest implements AbstractSmell { +public class MysteryGuest extends AbstractSmell { private List smellyElementList; @@ -111,7 +110,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; testMethod = new TestMethod(n.getNameAsString()); testMethod.setHasSmell(false); //default value is false (i.e. no smell) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java index 3c94c48..2d63024 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java @@ -9,7 +9,6 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -19,7 +18,7 @@ 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. This code checks the body of each test method if System.out. print(), println(), printf() and write() methods are called */ -public class PrintStatement implements AbstractSmell { +public class PrintStatement extends AbstractSmell { private List smellyElementList; @@ -68,7 +67,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; testMethod = new TestMethod(n.getNameAsString()); testMethod.setHasSmell(false); //default value is false (i.e. no smell) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java index f7eefce..49952fb 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java @@ -9,7 +9,6 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -18,7 +17,7 @@ /* If a test method contains an assert statement that explicitly returns a true or false, the method is marked as smelly */ -public class RedundantAssertion implements AbstractSmell { +public class RedundantAssertion extends AbstractSmell { private List smellyElementList; @@ -67,7 +66,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; testMethod = new TestMethod(n.getNameAsString()); testMethod.setHasSmell(false); //default value is false (i.e. no smell) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java index a6d7abb..5d1f0b6 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java @@ -12,13 +12,12 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; -public class ResourceOptimism implements AbstractSmell { +public class ResourceOptimism extends AbstractSmell { private List smellyElementList; @@ -72,13 +71,13 @@ 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.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); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java index 41fd68c..a4774e9 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java @@ -8,13 +8,12 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; -public class SensitiveEquality implements AbstractSmell { +public class SensitiveEquality extends AbstractSmell { private List smellyElementList; @@ -63,7 +62,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; testMethod = new TestMethod(n.getNameAsString()); testMethod.setHasSmell(false); //default value is false (i.e. no smell) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java index 4f7b38e..f361aa1 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java @@ -8,7 +8,6 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -18,7 +17,7 @@ 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 This code marks a method as smelly if the method body calls Thread.sleep() */ -public class SleepyTest implements AbstractSmell { +public class SleepyTest extends AbstractSmell { private List smellyElementList; @@ -67,7 +66,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; testMethod = new TestMethod(n.getNameAsString()); testMethod.setHasSmell(false); //default value is false (i.e. no smell) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java index 5c67f53..5d4754b 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java @@ -10,14 +10,13 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; import java.util.Optional; -public class UnknownTest implements AbstractSmell { +public class UnknownTest extends AbstractSmell { private List smellyElementList; @@ -70,7 +69,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++) { diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java index fec86ba..170e470 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java @@ -6,7 +6,6 @@ import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; -import edu.rit.se.testsmells.testsmell.Util; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -15,7 +14,7 @@ /* If a test methods contains a statements that exceeds a certain threshold, the method is marked as smelly */ -public class VerboseTest implements AbstractSmell { +public class VerboseTest extends AbstractSmell { private List smellyElementList; @@ -65,7 +64,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; testMethod = new TestMethod(n.getNameAsString()); testMethod.setHasSmell(false); //default value is false (i.e. no smell) From 287a2fbf59e83e5d8c35e8547635e9f353823230 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Wed, 26 Aug 2020 08:20:11 -0300 Subject: [PATCH 03/42] Pulled smellyElement member up to AbstractSmell, Moved with delegation the validation methods from AbstractSmell to MethodValidator --- .../testsmells/testsmell/AbstractSmell.java | 55 +++++++++---------- .../testsmells/testsmell/MethodValidator.java | 37 +++++++++++++ .../testsmell/smell/AssertionRoulette.java | 23 +------- .../testsmell/smell/ConditionalTestLogic.java | 23 +------- .../smell/ConstructorInitialization.java | 24 +------- .../testsmell/smell/DefaultTest.java | 25 +-------- .../testsmell/smell/DependentTest.java | 22 +------- .../testsmell/smell/DuplicateAssert.java | 22 +------- .../testsmells/testsmell/smell/EagerTest.java | 23 +------- .../testsmells/testsmell/smell/EmptyTest.java | 24 +------- .../smell/ExceptionCatchingThrowing.java | 24 +------- .../testsmell/smell/GeneralFixture.java | 22 +------- .../testsmell/smell/IgnoredTest.java | 28 ++-------- .../testsmells/testsmell/smell/LazyTest.java | 22 +------- .../testsmell/smell/MagicNumberTest.java | 24 +------- .../testsmell/smell/MysteryGuest.java | 22 +------- .../testsmell/smell/PrintStatement.java | 24 +------- .../testsmell/smell/RedundantAssertion.java | 24 +------- .../testsmell/smell/ResourceOptimism.java | 22 +------- .../testsmell/smell/SensitiveEquality.java | 24 +------- .../testsmell/smell/SleepyTest.java | 24 +------- .../testsmell/smell/UnknownTest.java | 22 +------- .../testsmell/smell/VerboseTest.java | 24 +------- 23 files changed, 125 insertions(+), 459 deletions(-) create mode 100644 src/main/java/edu/rit/se/testsmells/testsmell/MethodValidator.java diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java index e5f4833..c0591d9 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java @@ -1,36 +1,41 @@ package edu.rit.se.testsmells.testsmell; import com.github.javaparser.ast.CompilationUnit; -import com.github.javaparser.ast.Modifier; import com.github.javaparser.ast.body.MethodDeclaration; import java.io.FileNotFoundException; +import java.util.ArrayList; import java.util.List; public abstract class AbstractSmell { + private final MethodValidator methodValidator; + private final List smellyElementList; public abstract String getSmellName(); - public abstract boolean hasSmell(); - public abstract void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException; - public abstract List getSmellyElements(); + public AbstractSmell() { + methodValidator = new MethodValidator(); + smellyElementList = new ArrayList<>(); + } - protected boolean isValidTestMethod(MethodDeclaration n) { - boolean valid = false; + /** + * Returns the set of analyzed elements (i.e. test methods) + */ + public List getSmellyElements() { + return smellyElementList; + } - 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; - } - } - } + public void addSmellyElement(SmellyElement elem) { + smellyElementList.add(elem); + } - return valid; + /** + * Returns true if any of the elements has a smell + */ + public boolean hasSmell() { + return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; } protected boolean isNumber(String str) { @@ -42,19 +47,11 @@ protected boolean isNumber(String str) { } } - protected 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; - } - } - } + protected boolean isValidTestMethod(MethodDeclaration method) { + return methodValidator.isValidTestMethod(method); + } - return valid; + protected boolean isValidSetupMethod(MethodDeclaration method) { + return methodValidator.isValidSetupMethod(method); } } 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..c50d091 --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/MethodValidator.java @@ -0,0 +1,37 @@ +package edu.rit.se.testsmells.testsmell; + +import com.github.javaparser.ast.Modifier; +import com.github.javaparser.ast.body.MethodDeclaration; + +public class MethodValidator { + 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") || 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/smell/AssertionRoulette.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java index f326a94..6b231db 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java @@ -5,12 +5,9 @@ import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; 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. @@ -19,11 +16,10 @@ */ public class AssertionRoulette extends AbstractSmell { - private List smellyElementList; private int assertionsCount = 0; public AssertionRoulette() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -34,13 +30,6 @@ public String getSmellName() { return "Assertion Roulette"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for test methods for multiple assert statements without an explanation/message */ @@ -56,14 +45,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; @@ -90,7 +71,7 @@ else if (assertNoMessageCount >= 1) //if there is more than one assert statement testMethod.addDataItem("AssertCount", String.valueOf(assertNoMessageCount)); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java index b47eb8a..5d24b90 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java @@ -6,21 +6,17 @@ import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; 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; public ConditionalTestLogic() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -31,12 +27,6 @@ public String getSmellName() { return "Conditional Test Logic"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } /** * Analyze the test file for test methods that use conditional statements @@ -48,15 +38,6 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit 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 { private MethodDeclaration currentMethod = null; private int conditionCount, ifCount, switchCount, forCount, foreachCount, whileCount = 0; @@ -80,7 +61,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/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java index 8dedad5..a74428c 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java @@ -6,12 +6,9 @@ import com.github.javaparser.ast.type.ClassOrInterfaceType; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestClass; import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.List; /* @@ -19,12 +16,10 @@ 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; public ConstructorInitialization() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -35,13 +30,6 @@ public String getSmellName() { return "Constructor Initialization"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for Constructor Initialization smell */ @@ -53,14 +41,6 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit,CompilationUnit 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; @@ -82,7 +62,7 @@ public void visit(ConstructorDeclaration n, Void arg) { if(!constructorAllowed) { testClass = new TestClass(n.getNameAsString()); testClass.setHasSmell(true); - smellyElementList.add(testClass); + addSmellyElement(testClass); } } } 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 index 77ac41a..34591b1 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DefaultTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DefaultTest.java @@ -4,12 +4,9 @@ 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.SmellyElement; import edu.rit.se.testsmells.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 @@ -17,10 +14,9 @@ */ public class DefaultTest extends AbstractSmell { - private List smellyElementList; public DefaultTest() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -31,28 +27,13 @@ public String getSmellName() { return "Default Test"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - @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 { 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; @@ -61,7 +42,7 @@ 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); + addSmellyElement(testClass); } super.visit(n, arg); } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java index 62458bc..60602ba 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java @@ -5,7 +5,6 @@ import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -13,12 +12,12 @@ public class DependentTest extends AbstractSmell { - private List smellyElementList; + private List testMethods; public DependentTest() { - smellyElementList = new ArrayList<>(); + super(); testMethods = new ArrayList<>(); } @@ -30,13 +29,6 @@ public String getSmellName() { return "Dependent Test"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for test methods that call other test methods */ @@ -48,19 +40,11 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit for (TestMethod testMethod : testMethods) { if (testMethod.getCalledMethods().stream().anyMatch(x -> x.getName().equals(testMethods.stream().map(z -> z.getMethodDeclaration().getNameAsString())))){ - smellyElementList.add(new edu.rit.se.testsmells.testsmell.TestMethod(testMethod.getMethodDeclaration().getNameAsString())); + addSmellyElement(new edu.rit.se.testsmells.testsmell.TestMethod(testMethod.getMethodDeclaration().getNameAsString())); } } } - /** - * 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; List calledMethods; diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java index 777c703..ef9cb97 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java @@ -5,7 +5,6 @@ import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; @@ -16,10 +15,10 @@ public class DuplicateAssert extends AbstractSmell { - private List smellyElementList; + public DuplicateAssert() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -30,13 +29,6 @@ public String getSmellName() { return "Duplicate Assert"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for test methods that have multiple assert statements with the same explanation message */ @@ -47,14 +39,6 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit 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 { private MethodDeclaration currentMethod = null; @@ -83,7 +67,7 @@ public void visit(MethodDeclaration n, Void arg) { testMethod.setHasSmell(true); } - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java index dcfc97f..d7cf68d 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java @@ -10,7 +10,6 @@ import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; @@ -23,13 +22,13 @@ 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; public EagerTest() { + super(); productionMethods = new ArrayList<>(); - smellyElementList = new ArrayList<>(); } /** @@ -40,14 +39,6 @@ public String getSmellName() { return "Eager Test"; } - /** - * Returns true if any of the elements has a smell - */ - @Override - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for test methods that exhibit the 'Eager Test' smell */ @@ -67,14 +58,6 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit eagerCount = classVisitor.overallEager; } - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; - } - public int getEagerCount() { return eagerCount; } @@ -125,7 +108,7 @@ public void visit(MethodDeclaration n, Void arg) { 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; diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java index 676cd5b..89d778c 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java @@ -4,12 +4,9 @@ import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; 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) @@ -17,10 +14,10 @@ */ public class EmptyTest extends AbstractSmell { - private List smellyElementList; + public EmptyTest() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -31,13 +28,6 @@ public String getSmellName() { return "EmptyTest"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for test methods that are empty (i.e. no method body) */ @@ -48,14 +38,6 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit classVisitor.visit(testFileCompilationUnit, null); } - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; - } - /** * Visitor class */ @@ -79,7 +61,7 @@ public void visit(MethodDeclaration n, Void arg) { } } } - smellyElementList.add(testMethod); + addSmellyElement(testMethod); } } } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java index e15fa1f..06e18ea 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java @@ -6,12 +6,9 @@ import com.github.javaparser.ast.stmt.ThrowStmt; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; 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 @@ -19,10 +16,10 @@ */ public class ExceptionCatchingThrowing extends AbstractSmell { - private List smellyElementList; + public ExceptionCatchingThrowing() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -33,13 +30,6 @@ public String getSmellName() { return "Exception Catching Throwing"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for test methods that have exception handling */ @@ -50,14 +40,6 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit 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 { private MethodDeclaration currentMethod = null; private int exceptionCount = 0; @@ -79,7 +61,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/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java index 83ee3bb..c83977e 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java @@ -12,7 +12,6 @@ import com.github.javaparser.ast.stmt.ExpressionStmt; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; @@ -20,14 +19,14 @@ public class GeneralFixture extends AbstractSmell { - private List smellyElementList; + List methodList; MethodDeclaration setupMethod; List fieldList; List setupFields; public GeneralFixture() { - smellyElementList = new ArrayList<>(); + super(); methodList = new ArrayList<>(); fieldList = new ArrayList<>(); setupFields = new ArrayList<>(); @@ -41,13 +40,6 @@ public String getSmellName() { return "General Fixture"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - @Override public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { GeneralFixture.ClassVisitor classVisitor; @@ -84,14 +76,6 @@ 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 currentMethod = null; @@ -137,7 +121,7 @@ public void visit(MethodDeclaration n, Void arg) { testMethod = new TestMethod(n.getNameAsString()); testMethod.setHasSmell(fixtureCount.size() != setupFields.size()); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); fixtureCount = new HashSet<>(); currentMethod = null; diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java index 361e6b7..4beae16 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java @@ -6,20 +6,17 @@ import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; 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; + public IgnoredTest() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -30,13 +27,6 @@ public String getSmellName() { return "IgnoredTest"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for test methods that contain Ignored test methods */ @@ -47,14 +37,6 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit classVisitor.visit(testFileCompilationUnit, null); } - /** - * Returns the set of analyzed elements (i.e. test methods) - */ - @Override - public List getSmellyElements() { - return smellyElementList; - } - /** * Visitor class */ @@ -70,7 +52,7 @@ public void visit(ClassOrInterfaceDeclaration n, Void arg) { if (n.getAnnotationByName("Ignore").isPresent()) { testClass = new TestClass(n.getNameAsString()); testClass.setHasSmell(true); - smellyElementList.add(testClass); + addSmellyElement(testClass); } super.visit(n, arg); } @@ -87,7 +69,7 @@ public void visit(MethodDeclaration n, Void arg) { if (n.getAnnotationByName("Ignore").isPresent()) { testMethod = new TestMethod(n.getNameAsString()); testMethod.setHasSmell(true); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); return; } } @@ -98,7 +80,7 @@ public void visit(MethodDeclaration n, Void arg) { if (!n.getModifiers().contains(Modifier.PUBLIC)) { testMethod = new TestMethod(n.getNameAsString()); testMethod.setHasSmell(true); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); return; } } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java index cf96f70..c64b439 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java @@ -10,7 +10,6 @@ import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; @@ -23,13 +22,13 @@ 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; public LazyTest() { + super(); productionMethods = new ArrayList<>(); - smellyElementList = new ArrayList<>(); calledProductionMethods = new ArrayList<>(); } @@ -41,13 +40,6 @@ public String getSmellName() { return "Lazy Test"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for test methods that exhibit the 'Lazy Test' smell */ @@ -73,20 +65,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; diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java index fe873be..5f06e03 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java @@ -7,19 +7,16 @@ import com.github.javaparser.ast.expr.ObjectCreationExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.List; public class MagicNumberTest extends AbstractSmell { - private List smellyElementList; + public MagicNumberTest() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -30,13 +27,6 @@ public String getSmellName() { return "Magic Number Test"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for test methods that have magic numbers in as parameters in the assert methods */ @@ -47,14 +37,6 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit 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 { private MethodDeclaration currentMethod = null; TestMethod testMethod; @@ -72,7 +54,7 @@ public void visit(MethodDeclaration n, Void arg) { testMethod.setHasSmell(magicCount >= 1); testMethod.addDataItem("MagicNumberCount", String.valueOf(magicCount)); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java index d763a5d..91c0484 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java @@ -6,7 +6,6 @@ import com.github.javaparser.ast.expr.VariableDeclarationExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; @@ -23,10 +22,10 @@ */ public class MysteryGuest extends AbstractSmell { - private List smellyElementList; + public MysteryGuest() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -37,13 +36,6 @@ public String getSmellName() { return "Mystery Guest"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for test methods that use external resources */ @@ -54,14 +46,6 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit 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 { private List mysteryTypes = new ArrayList<>( Arrays.asList( @@ -119,7 +103,7 @@ public void visit(MethodDeclaration n, Void arg) { testMethod.setHasSmell(mysteryCount > 0); testMethod.addDataItem("MysteryCount", String.valueOf(mysteryCount)); - smellyElementList.add(testMethod); + addSmellyElement(testMethod); //reset values for next method currentMethod = null; diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java index 2d63024..60fead2 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java @@ -7,12 +7,9 @@ import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; 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. @@ -20,10 +17,10 @@ This code checks the body of each test method if System.out. print(), println(), */ public class PrintStatement extends AbstractSmell { - private List smellyElementList; + public PrintStatement() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -34,13 +31,6 @@ public String getSmellName() { return "Print Statement"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for test methods that print output to the console */ @@ -51,14 +41,6 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit 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 { private MethodDeclaration currentMethod = null; private int printCount = 0; @@ -76,7 +58,7 @@ public void visit(MethodDeclaration n, Void 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/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java index 49952fb..f4a1446 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java @@ -7,22 +7,19 @@ import com.github.javaparser.ast.expr.NullLiteralExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; 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; + public RedundantAssertion() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -33,13 +30,6 @@ public String getSmellName() { return "Redundant Assertion"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for test methods for multiple assert statements */ @@ -50,14 +40,6 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit 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 { private MethodDeclaration currentMethod = null; private int redundantCount = 0; @@ -75,7 +57,7 @@ public void visit(MethodDeclaration n, Void 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/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java index 5d1f0b6..721df1f 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java @@ -10,7 +10,6 @@ import com.github.javaparser.ast.expr.VariableDeclarationExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; @@ -19,10 +18,10 @@ public class ResourceOptimism extends AbstractSmell { - private List smellyElementList; + public ResourceOptimism() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -33,13 +32,6 @@ public String getSmellName() { return "Resource Optimism"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for the 'ResourceOptimism' smell */ @@ -50,14 +42,6 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit 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 { private MethodDeclaration currentMethod = null; @@ -80,7 +64,7 @@ public void visit(MethodDeclaration n, Void arg) { 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/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java index a4774e9..5d93bb8 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java @@ -6,19 +6,16 @@ import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; 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; + public SensitiveEquality() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -29,13 +26,6 @@ public String getSmellName() { return "Sensitive Equality"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for test methods the 'Sensitive Equality' smell */ @@ -46,14 +36,6 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit 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 { private MethodDeclaration currentMethod = null; private int sensitiveCount = 0; @@ -71,7 +53,7 @@ public void visit(MethodDeclaration n, Void 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/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java index f361aa1..bb9651c 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java @@ -6,12 +6,9 @@ import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; 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 @@ -19,10 +16,10 @@ */ public class SleepyTest extends AbstractSmell { - private List smellyElementList; + public SleepyTest() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -33,13 +30,6 @@ public String getSmellName() { return "Sleepy Test"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for test methods that use Thread.sleep() */ @@ -50,14 +40,6 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit 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 { private MethodDeclaration currentMethod = null; private int sleepCount = 0; @@ -75,7 +57,7 @@ public void visit(MethodDeclaration n, Void 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/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java index 5d4754b..70b6155 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java @@ -8,7 +8,6 @@ import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; @@ -18,10 +17,10 @@ public class UnknownTest extends AbstractSmell { - private List smellyElementList; + public UnknownTest() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -32,13 +31,6 @@ public String getSmellName() { return "Unknown Test"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for test methods that do not have assert statement or exceptions */ @@ -49,14 +41,6 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit 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 { private MethodDeclaration currentMethod = null; @@ -91,7 +75,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/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java index 170e470..045b415 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java @@ -4,22 +4,19 @@ import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; -import edu.rit.se.testsmells.testsmell.SmellyElement; 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; + public VerboseTest() { - smellyElementList = new ArrayList<>(); + super(); } /** @@ -30,13 +27,6 @@ public String getSmellName() { return "Verbose Test"; } - /** - * Returns true if any of the elements has a smell - */ - public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; - } - /** * Analyze the test file for test methods for the 'Verbose Test' smell */ @@ -47,14 +37,6 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit 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 { final int MAX_STATEMENTS = 123; private MethodDeclaration currentMethod = null; @@ -81,7 +63,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; From 9ba345a9e00704b7efed49617c7d013aa4e69d65 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Wed, 26 Aug 2020 09:50:40 -0300 Subject: [PATCH 04/42] Refactored Main.java, renamed some methods (TestSmellDetector.addSmell -> addDetectableSmell, TestFile.addSmell -> addDetectedSmell, ResultsWriter.writeColumnName -> writeColumnNames) --- src/main/java/edu/rit/se/testsmells/Main.java | 171 +++++++++--------- .../testsmells/testsmell/ResultsWriter.java | 2 +- .../rit/se/testsmells/testsmell/TestFile.java | 2 +- .../testsmell/TestSmellDetector.java | 7 +- 4 files changed, 96 insertions(+), 86 deletions(-) diff --git a/src/main/java/edu/rit/se/testsmells/Main.java b/src/main/java/edu/rit/se/testsmells/Main.java index a767659..6a2a11d 100644 --- a/src/main/java/edu/rit/se/testsmells/Main.java +++ b/src/main/java/edu/rit/se/testsmells/Main.java @@ -10,7 +10,6 @@ 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; @@ -18,26 +17,43 @@ 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; - } - } + File inputFile = handleCliArgs(args); TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); initializeSmells(testSmellDetector); + ResultsWriter resultsWriter = initializeOutputFile(testSmellDetector.getTestSmellNames()); + /* - Read the input file and build the TestFile objects - */ - BufferedReader in = new BufferedReader(new FileReader(args[0])); + Iterate through all test files to detect smells and then write the output + */ + for (TestFile rawFile : readInputTestFiles(inputFile)) { + System.out.println(getCurrentDateFormatted() + " Processing: " + rawFile.getTestFilePath()); + System.out.println("Processing: " + rawFile.getTestFilePath()); + + TestFile smellyFile = testSmellDetector.detectSmells(rawFile); + + writeOutput(resultsWriter, smellyFile); + } + + System.out.println("end"); + } + + private static void writeOutput(ResultsWriter resultsWriter, TestFile smellyFile) throws IOException { + List entries = getTestDescriptionEntries(smellyFile); + for (AbstractSmell smell : smellyFile.getTestSmells()) { + try { + entries.add(String.valueOf(smell.hasSmell())); + } catch (NullPointerException e) { + entries.add(""); + } + } + resultsWriter.writeLine(entries); + } + + private static List readInputTestFiles(File inputFile) throws IOException { + BufferedReader in = new BufferedReader(new FileReader(inputFile)); String str; String[] lineItem; @@ -56,81 +72,74 @@ public static void main(String[] args) throws IOException { testFiles.add(testFile); } + return testFiles; + } - /* - Initialize the output file - Create the output file and add the column names - */ + 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 Object getCurrentDateFormatted() { + return (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()); + } + + private static List getTestDescriptionEntries(TestFile smellyFile) { + List columnValues = new ArrayList<>(); + + columnValues.add(smellyFile.getApp()); + columnValues.add(smellyFile.getTestFileName()); + columnValues.add(smellyFile.getTestFilePath()); + columnValues.add(smellyFile.getProductionFilePath()); + columnValues.add(smellyFile.getRelativeTestFilePath()); + columnValues.add(smellyFile.getRelativeProductionFilePath()); + + return columnValues; + } + + private static ResultsWriter initializeOutputFile(List testSmellNames) throws IOException { ResultsWriter resultsWriter = ResultsWriter.createResultsWriter(); - List columnNames; - List columnValues; + List columnNames = new ArrayList<>(); - 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"); + columnNames.add("App"); + columnNames.add("TestClass"); + columnNames.add("TestFilePath"); + columnNames.add("ProductionFilePath"); + columnNames.add("RelativeTestFilePath"); + columnNames.add("RelativeProductionFilePath"); - resultsWriter.writeColumnName(columnNames); + columnNames.addAll(testSmellNames); - /* - 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.hasSmell())); - } catch (NullPointerException e) { - columnValues.add(""); - } - } - resultsWriter.writeLine(columnValues); - } + resultsWriter.writeColumnNames(columnNames); - System.out.println("end"); + return resultsWriter; } private static void initializeSmells(TestSmellDetector testSmellDetector) { - testSmellDetector.addSmell(new AssertionRoulette()); - testSmellDetector.addSmell(new ConditionalTestLogic()); - testSmellDetector.addSmell(new ConstructorInitialization()); - testSmellDetector.addSmell(new DefaultTest()); - testSmellDetector.addSmell(new EmptyTest()); - testSmellDetector.addSmell(new ExceptionCatchingThrowing()); - testSmellDetector.addSmell(new GeneralFixture()); - testSmellDetector.addSmell(new MysteryGuest()); - testSmellDetector.addSmell(new PrintStatement()); - testSmellDetector.addSmell(new RedundantAssertion()); - testSmellDetector.addSmell(new SensitiveEquality()); - testSmellDetector.addSmell(new VerboseTest()); - testSmellDetector.addSmell(new SleepyTest()); - testSmellDetector.addSmell(new EagerTest()); - testSmellDetector.addSmell(new LazyTest()); - testSmellDetector.addSmell(new DuplicateAssert()); - testSmellDetector.addSmell(new UnknownTest()); - testSmellDetector.addSmell(new IgnoredTest()); - testSmellDetector.addSmell(new ResourceOptimism()); - testSmellDetector.addSmell(new MagicNumberTest()); - testSmellDetector.addSmell(new DependentTest()); + 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()); } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java index 0f26dd2..019dc7e 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java @@ -41,7 +41,7 @@ public static ResultsWriter createResultsWriter() throws IOException { * @param columnNames the column names * @throws IOException */ - public void writeColumnName(List columnNames) throws IOException { + public void writeColumnNames(List columnNames) throws IOException { writeOutput(columnNames); } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java index 70cd9ba..8e7ee16 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java @@ -37,7 +37,7 @@ public TestFile(String app, String testFilePath, String productionFilePath) { this.testSmells = new ArrayList<>(); } - public void addSmell(AbstractSmell smell) { + public void addDetectedSmell(AbstractSmell smell) { testSmells.add(smell); } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java index 5219574..9e1e19a 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java @@ -22,7 +22,7 @@ public TestSmellDetector() { testSmells = new ArrayList<>(); } - public void addSmell(AbstractSmell smell) { + public void addDetectableSmell(AbstractSmell smell) { testSmells.add(smell); } @@ -55,9 +55,10 @@ public TestFile detectSmells(TestFile testFile) throws IOException { for (AbstractSmell smell : testSmells) { try { smell.runAnalysis(testFileCompilationUnit, productionFileCompilationUnit, testFile.getTestFileNameWithoutExtension(), testFile.getProductionFileNameWithoutExtension()); - testFile.addSmell(smell); + testFile.addDetectedSmell(smell); + //TODO: Use smell.getSmellyElements() to aggregate smelly classes and methods } finally { - testFile.addSmell(null); + testFile.addDetectedSmell(null); } } From 3497f8294667fa8be72298efa993262f3f95df5f Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Wed, 26 Aug 2020 14:23:16 -0300 Subject: [PATCH 05/42] Implemented MethodValidator and TestSmellDetector unit tests, added 'BeforeEach' as setUp annotation and empty catch block on detectSmells (preventing FileNotFoundException propagation) --- .../testsmells/testsmell/MethodValidator.java | 2 +- .../testsmell/TestSmellDetector.java | 1 + .../testsmell/MethodValidatorTest.java | 177 ++++++++++++++++++ .../testsmell/TestSmellDetectorTest.java | 79 ++++++++ 4 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/MethodValidator.java b/src/main/java/edu/rit/se/testsmells/testsmell/MethodValidator.java index c50d091..02709bb 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/MethodValidator.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/MethodValidator.java @@ -9,7 +9,7 @@ public boolean isValidTestMethod(MethodDeclaration method) { } public boolean isValidSetupMethod(MethodDeclaration method) { - return isVisible(method) && (hasAnnotation(method, "Before") || nameStartsWith(method, "setUp")); + return isVisible(method) && (hasAnnotation(method, "Before") || hasAnnotation(method, "BeforeEach") || nameStartsWith(method, "setUp")); } private boolean nameStartsWith(MethodDeclaration method, String value) { diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java index 9e1e19a..385b8fe 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java @@ -57,6 +57,7 @@ public TestFile detectSmells(TestFile testFile) throws IOException { smell.runAnalysis(testFileCompilationUnit, productionFileCompilationUnit, testFile.getTestFileNameWithoutExtension(), testFile.getProductionFileNameWithoutExtension()); testFile.addDetectedSmell(smell); //TODO: Use smell.getSmellyElements() to aggregate smelly classes and methods + } catch (FileNotFoundException ignored) { } finally { testFile.addDetectedSmell(null); } 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..64ca187 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java @@ -0,0 +1,177 @@ +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.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class MethodValidatorTest { + MethodValidator sut; + + @BeforeEach + void setUp() { + sut = new MethodValidator(); + } + + @Test + void isValidTestMethod_ignored() { + String code = + "class nothing { \n" + + "@Ignore \n" + + "@Test \n" + + "public void sampleTest(){} \n" + + "} \n"; + + MethodVisitorStub assertion = new MethodVisitorStub((MethodDeclaration a) -> assertFalse(sut.isValidTestMethod(a))); + + 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((MethodDeclaration a) -> assertFalse(sut.isValidTestMethod(a))); + + 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((MethodDeclaration a) -> assertTrue(sut.isValidTestMethod(a))); + + assertion.visit(JavaParser.parse(code), null); + } + + @Test + void isValidTestMethod_startsWithTest() { + String code = + "class nothing { \n" + + "public void testSample(){} \n" + + "} \n"; + + MethodVisitorStub assertion = new MethodVisitorStub((MethodDeclaration a) -> assertTrue(sut.isValidTestMethod(a))); + + assertion.visit(JavaParser.parse(code), null); + } + + @Test + void isValidTestMethod_visibleNonTestMethod() { + String code = + "class nothing { \n" + + "public void sampleTest(){} \n" + + "} \n"; + + MethodVisitorStub assertion = new MethodVisitorStub((MethodDeclaration a) -> assertFalse(sut.isValidTestMethod(a))); + + 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((MethodDeclaration a) -> assertFalse(sut.isValidSetupMethod(a))); + + 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((MethodDeclaration a) -> assertFalse(sut.isValidSetupMethod(a))); + + 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((MethodDeclaration a) -> assertTrue(sut.isValidSetupMethod(a))); + + 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((MethodDeclaration a) -> assertTrue(sut.isValidSetupMethod(a))); + + assertion.visit(JavaParser.parse(code), null); + } + + @Test + void isValidSetupMethod_namedSetUp() { + String code = + "class nothing { \n" + + "public void setUp(){} \n" + + "} \n"; + + MethodVisitorStub assertion = new MethodVisitorStub((MethodDeclaration a) -> assertTrue(sut.isValidSetupMethod(a))); + + 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((MethodDeclaration a) -> assertFalse(sut.isValidSetupMethod(a))); + + 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) { + super.visit(n, arg); + func.accept(n); + } + } +} \ No newline at end of file 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..93e55da --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java @@ -0,0 +1,79 @@ +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() { + TestFile tf = new TestFileStub("app", "", ""); + List smells = new ArrayList<>(); + + try { + smells = this.sut.detectSmells(tf).getTestSmells(); + } catch (IOException e) { + fail("Exception IOException unexpectedly raised: " + e.getMessage()); + } + + assertEquals(smells.size(), 1); + assertNull(smells.get(0)); + } + + private static class TestFileStub extends TestFile { + public TestFileStub(String app, String testFilePath, String productionFilePath) { + super(app, testFilePath, productionFilePath); + } + + @Override + public String getTestFileNameWithoutExtension() { + return ""; + } + + @Override + public String getProductionFileNameWithoutExtension() { + return ""; + } + } + + private static class TestSmellStub extends AbstractSmell { + + @Override + public String getSmellName() { + return TSStubName; + } + + @Override + public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { + throw new FileNotFoundException("test"); + } + } +} \ No newline at end of file From b67eed0ba1282a8c45f2af8a83486ea3c4c6fa33 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Thu, 27 Aug 2020 14:23:00 -0300 Subject: [PATCH 06/42] Removed writeOutput aliases on ResultsWriter, renamed writeOutput to writeCSV, moved output file handling methods from Main to ResultsWriter, related CSV header definition with properties through HashMap on TestFile.getTestDescriptionEntries method --- src/main/java/edu/rit/se/testsmells/Main.java | 69 ++++--------------- .../testsmells/testsmell/ResultsWriter.java | 39 +++++------ .../rit/se/testsmells/testsmell/TestFile.java | 54 ++++++++------- 3 files changed, 62 insertions(+), 100 deletions(-) diff --git a/src/main/java/edu/rit/se/testsmells/Main.java b/src/main/java/edu/rit/se/testsmells/Main.java index 6a2a11d..175661b 100644 --- a/src/main/java/edu/rit/se/testsmells/Main.java +++ b/src/main/java/edu/rit/se/testsmells/Main.java @@ -1,6 +1,5 @@ package edu.rit.se.testsmells; -import edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.ResultsWriter; import edu.rit.se.testsmells.testsmell.TestFile; import edu.rit.se.testsmells.testsmell.TestSmellDetector; @@ -19,37 +18,26 @@ public class Main { public static void main(String[] args) throws IOException { File inputFile = handleCliArgs(args); - TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); - - initializeSmells(testSmellDetector); - - ResultsWriter resultsWriter = initializeOutputFile(testSmellDetector.getTestSmellNames()); + TestSmellDetector testSmellDetector = initializeSmells(); - /* - Iterate through all test files to detect smells and then write the output - */ - for (TestFile rawFile : readInputTestFiles(inputFile)) { + List files = readInputTestFiles(inputFile); + ResultsWriter resultsWriter = initializeOutputFile(testSmellDetector, files.get(0)); + for (TestFile rawFile : files) { System.out.println(getCurrentDateFormatted() + " Processing: " + rawFile.getTestFilePath()); - System.out.println("Processing: " + rawFile.getTestFilePath()); - TestFile smellyFile = testSmellDetector.detectSmells(rawFile); - writeOutput(resultsWriter, smellyFile); + resultsWriter.exportSmellsToFile(smellyFile); } System.out.println("end"); } - private static void writeOutput(ResultsWriter resultsWriter, TestFile smellyFile) throws IOException { - List entries = getTestDescriptionEntries(smellyFile); - for (AbstractSmell smell : smellyFile.getTestSmells()) { - try { - entries.add(String.valueOf(smell.hasSmell())); - } catch (NullPointerException e) { - entries.add(""); - } - } - resultsWriter.writeLine(entries); + private static ResultsWriter initializeOutputFile(TestSmellDetector testSmellDetector, TestFile anyFile) throws IOException { + ResultsWriter resultsWriter = ResultsWriter.createResultsWriter(); + + resultsWriter.writeCSVHeader(testSmellDetector, anyFile); + + return resultsWriter; } private static List readInputTestFiles(File inputFile) throws IOException { @@ -87,38 +75,9 @@ private static Object getCurrentDateFormatted() { return (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()); } - private static List getTestDescriptionEntries(TestFile smellyFile) { - List columnValues = new ArrayList<>(); - - columnValues.add(smellyFile.getApp()); - columnValues.add(smellyFile.getTestFileName()); - columnValues.add(smellyFile.getTestFilePath()); - columnValues.add(smellyFile.getProductionFilePath()); - columnValues.add(smellyFile.getRelativeTestFilePath()); - columnValues.add(smellyFile.getRelativeProductionFilePath()); - - return columnValues; - } - - private static ResultsWriter initializeOutputFile(List testSmellNames) throws IOException { - ResultsWriter resultsWriter = ResultsWriter.createResultsWriter(); - List columnNames = new ArrayList<>(); - - columnNames.add("App"); - columnNames.add("TestClass"); - columnNames.add("TestFilePath"); - columnNames.add("ProductionFilePath"); - columnNames.add("RelativeTestFilePath"); - columnNames.add("RelativeProductionFilePath"); - - columnNames.addAll(testSmellNames); - - resultsWriter.writeColumnNames(columnNames); - - return resultsWriter; - } + private static TestSmellDetector initializeSmells() { + TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); - private static void initializeSmells(TestSmellDetector testSmellDetector) { testSmellDetector.addDetectableSmell(new AssertionRoulette()); testSmellDetector.addDetectableSmell(new ConditionalTestLogic()); testSmellDetector.addDetectableSmell(new ConstructorInitialization()); @@ -140,6 +99,8 @@ private static void initializeSmells(TestSmellDetector testSmellDetector) { 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/ResultsWriter.java b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java index 019dc7e..02de82e 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java @@ -3,6 +3,7 @@ import java.io.FileWriter; import java.io.IOException; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Calendar; import java.util.List; @@ -17,7 +18,7 @@ public class ResultsWriter { /** * Creates the file into which output it to be written into. Results from each file will be stored in a new file * - * @throws IOException + * @throws IOException Failed to create/open output file */ private ResultsWriter() throws IOException { String time = String.valueOf(Calendar.getInstance().getTimeInMillis()); @@ -29,39 +30,37 @@ private ResultsWriter() throws IOException { * Factory method that provides a new instance of the ResultsWriter * * @return new ResultsWriter instance - * @throws IOException + * @throws IOException Failed to create/open output file */ 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 writeColumnNames(List columnNames) throws IOException { - writeOutput(columnNames); + public void writeCSVHeader(TestSmellDetector testSmellDetector, TestFile anyFile) throws IOException { + List headers = new ArrayList<>(anyFile.getTestDescriptionEntries().keySet()); + headers.addAll(testSmellDetector.getTestSmellNames()); + writeCSV(headers); } - /** - * Writes column values into the CSV file - * - * @param columnValues the column values - * @throws IOException - */ - public void writeLine(List columnValues) throws IOException { - writeOutput(columnValues); + public void exportSmellsToFile(TestFile 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 + * @throws IOException Failed to create/open output file */ - private void writeOutput(List dataValues) throws IOException { + public void writeCSV(List dataValues) throws IOException { writer = new FileWriter(outputFile, true); for (int i = 0; i < dataValues.size(); i++) { diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java index 8e7ee16..2033d07 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java @@ -4,50 +4,52 @@ import java.io.File; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class TestFile { - private String app, testFilePath, productionFilePath; - private List testSmells; + private final String app, testFilePath, productionFilePath; + private final List testSmells; - public String getApp() { - return app; + public TestFile(String app, String testFilePath, String productionFilePath) { + this.app = app; + this.testFilePath = testFilePath; + this.productionFilePath = productionFilePath; + this.testSmells = new ArrayList<>(); } - public String getProductionFilePath() { - return productionFilePath; + 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("RelativeTestFilePath", getRelativeTestFilePath()); + descriptions.put("RelativeProductionFilePath", getRelativeProductionFilePath()); + + return descriptions; } - public String getTestFilePath() { - return testFilePath; + public void addDetectedSmell(AbstractSmell smell) { + testSmells.add(smell); } public List getTestSmells() { return testSmells; } - public boolean hasProductionFile() { - 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 String getApp() { + return app; } - public void addDetectedSmell(AbstractSmell smell) { - testSmells.add(smell); + public String getProductionFilePath() { + return productionFilePath; } - /** - * 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 getTestFilePath() { + return testFilePath; } public String getTestFileName() { From 0a8a036bf136afafa882a679b4c88ba17ae88ec6 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Thu, 27 Aug 2020 15:08:33 -0300 Subject: [PATCH 07/42] Extracted super class SmellsContainer from TestFile (use it on ResultsWriter), removed code duplication by extracting methods (TestFile.removeExtension and TestFile.extractFileFromPath), removed TestSmellDetector.detectSmells return confusion (it was returning the param it received) --- src/main/java/edu/rit/se/testsmells/Main.java | 8 +-- .../testsmells/testsmell/ResultsWriter.java | 4 +- .../testsmells/testsmell/SmellsContainer.java | 23 +++++++++ .../rit/se/testsmells/testsmell/TestFile.java | 51 +++++++++---------- .../testsmell/TestSmellDetector.java | 4 +- .../testsmell/TestSmellDetectorTest.java | 3 +- 6 files changed, 57 insertions(+), 36 deletions(-) create mode 100644 src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java diff --git a/src/main/java/edu/rit/se/testsmells/Main.java b/src/main/java/edu/rit/se/testsmells/Main.java index 175661b..f0c7a95 100644 --- a/src/main/java/edu/rit/se/testsmells/Main.java +++ b/src/main/java/edu/rit/se/testsmells/Main.java @@ -22,11 +22,11 @@ public static void main(String[] args) throws IOException { List files = readInputTestFiles(inputFile); ResultsWriter resultsWriter = initializeOutputFile(testSmellDetector, files.get(0)); - for (TestFile rawFile : files) { - System.out.println(getCurrentDateFormatted() + " Processing: " + rawFile.getTestFilePath()); - TestFile smellyFile = testSmellDetector.detectSmells(rawFile); + for (TestFile file : files) { + System.out.println(getCurrentDateFormatted() + " Processing: " + file.getTestFilePath()); - resultsWriter.exportSmellsToFile(smellyFile); + testSmellDetector.detectSmells(file); + resultsWriter.exportSmells(file); } System.out.println("end"); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java index 02de82e..4e26f75 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java @@ -36,13 +36,13 @@ public static ResultsWriter createResultsWriter() throws IOException { return new ResultsWriter(); } - public void writeCSVHeader(TestSmellDetector testSmellDetector, TestFile anyFile) throws IOException { + public void writeCSVHeader(TestSmellDetector testSmellDetector, SmellsContainer anyFile) throws IOException { List headers = new ArrayList<>(anyFile.getTestDescriptionEntries().keySet()); headers.addAll(testSmellDetector.getTestSmellNames()); writeCSV(headers); } - public void exportSmellsToFile(TestFile fileTestSmells) throws IOException { + public void exportSmells(SmellsContainer fileTestSmells) throws IOException { List entries = new ArrayList<>(fileTestSmells.getTestDescriptionEntries().values()); for (AbstractSmell smell : fileTestSmells.getTestSmells()) { try { 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..f88bf89 --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java @@ -0,0 +1,23 @@ +package edu.rit.se.testsmells.testsmell; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public abstract class SmellsContainer { + protected final List testSmells; + + public SmellsContainer() { + this.testSmells = new ArrayList<>(); + } + + public abstract Map getTestDescriptionEntries(); + + public void addDetectedSmell(AbstractSmell smell) { + testSmells.add(smell); + } + + public List getTestSmells() { + return testSmells; + } +} diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java index 2033d07..d2f3757 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java @@ -3,20 +3,17 @@ import org.apache.commons.lang3.StringUtils; import java.io.File; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; -public class TestFile { +public class TestFile extends SmellsContainer { private final String app, testFilePath, productionFilePath; - private final List testSmells; public TestFile(String app, String testFilePath, String productionFilePath) { + super(); this.app = app; this.testFilePath = testFilePath; this.productionFilePath = productionFilePath; - this.testSmells = new ArrayList<>(); } public Map getTestDescriptionEntries() { @@ -32,14 +29,6 @@ public Map getTestDescriptionEntries() { return descriptions; } - public void addDetectedSmell(AbstractSmell smell) { - testSmells.add(smell); - } - - public List getTestSmells() { - return testSmells; - } - public String getApp() { return app; } @@ -52,26 +41,36 @@ public String getTestFilePath() { return testFilePath; } - 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); + return removeExtension(getTestFileName()); } public String getProductionFileNameWithoutExtension() { - int lastIndex = getProductionFileName().lastIndexOf("."); - if (lastIndex == -1) return ""; - return getProductionFileName().substring(0, lastIndex); + return removeExtension(getProductionFileName()); + } + + private String removeExtension(String filename) { + try { + return filename.substring(0, filename.lastIndexOf(".")); + } catch (StringIndexOutOfBoundsException e) { + return ""; + } + } + + private String extractFileFromPath(String path) { + try { + return path.substring(path.lastIndexOf(File.separator) + 1); + } catch (StringIndexOutOfBoundsException e) { + return ""; + } + } + + public String getTestFileName() { + return extractFileFromPath(testFilePath); } public String getProductionFileName() { - int lastIndex = productionFilePath.lastIndexOf(File.separator); - if (lastIndex == -1) return ""; - return productionFilePath.substring(lastIndex + 1); + return extractFileFromPath(productionFilePath); } /** diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java index 385b8fe..807995e 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java @@ -47,7 +47,7 @@ public List getTestSmellNames() { /** * 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 { + public void detectSmells(TestFile testFile) throws IOException { CompilationUnit testFileCompilationUnit = parseIntoCompilationUnit(testFile.getTestFilePath()); CompilationUnit productionFileCompilationUnit = parseIntoCompilationUnit(testFile.getProductionFilePath()); @@ -63,8 +63,6 @@ public TestFile detectSmells(TestFile testFile) throws IOException { } } - return testFile; - } private CompilationUnit parseIntoCompilationUnit(String testFilePath) throws FileNotFoundException { diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java index 93e55da..d2862e2 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java @@ -39,7 +39,8 @@ void detectSmells() { List smells = new ArrayList<>(); try { - smells = this.sut.detectSmells(tf).getTestSmells(); + this.sut.detectSmells(tf); + smells = tf.getTestSmells(); } catch (IOException e) { fail("Exception IOException unexpectedly raised: " + e.getMessage()); } From 3e7eb48008ac75fa05ddaf004a093d11b7ec1b55 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Thu, 27 Aug 2020 15:51:21 -0300 Subject: [PATCH 08/42] A relative file path without separator is equal to the filename, a filename without "." (dot) remain the same when its extension is removed --- .../rit/se/testsmells/testsmell/TestFile.java | 8 +- .../se/testsmells/testsmell/TestFileTest.java | 89 +++++++++++++++++++ 2 files changed, 91 insertions(+), 6 deletions(-) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java index d2f3757..ce0e447 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java @@ -53,16 +53,12 @@ private String removeExtension(String filename) { try { return filename.substring(0, filename.lastIndexOf(".")); } catch (StringIndexOutOfBoundsException e) { - return ""; + return filename; } } private String extractFileFromPath(String path) { - try { - return path.substring(path.lastIndexOf(File.separator) + 1); - } catch (StringIndexOutOfBoundsException e) { - return ""; - } + return path.substring(path.lastIndexOf(File.separator) + 1); } public String getTestFileName() { diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/TestFileTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestFileTest.java index df05f0c..fdda2ea 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/TestFileTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestFileTest.java @@ -5,6 +5,8 @@ import org.junit.jupiter.api.condition.EnabledOnOs; import org.junit.jupiter.api.condition.OS; +import java.io.File; + import static org.junit.jupiter.api.Assertions.assertEquals; class TestFileTest { @@ -89,4 +91,91 @@ public void testGetRelativeTestFilePathWindows() { String output = testFileWindows.getRelativeTestFilePath(); assertEquals(oracle, output); } +} + +class FilePathWithoutSeparatorTest { + String path; + TestFile sut; + + @BeforeEach + public void setUp() { + path = "file.extension"; + sut = new TestFile("", path, path); + } + + @Test + public void testTestFilePath() { + assertEquals(sut.getTestFilePath(), path); + } + + @Test + public void testTestFileName() { + assertEquals(sut.getTestFileName(), path); + } + + @Test + public void testProductionFilePath() { + assertEquals(sut.getProductionFilePath(), path); + } + + @Test + public void testProductionFileName() { + assertEquals(sut.getProductionFileName(), path); + } +} + +class FileWithoutExtension { + String filename; + String relativePath; + String path; + TestFile sut; + + @BeforeEach + public void setUp() { + String app = "folder"; + filename = "fileWithoutExtension"; + relativePath = "to" + File.separator + filename; + path = app + File.separator + relativePath; + sut = new TestFile(app, path, path); + } + + @Test + public void testTestFilePath() { + assertEquals(sut.getTestFilePath(), path); + } + + @Test + public void testRelativeTestFilePath() { + assertEquals(sut.getRelativeTestFilePath(), relativePath); + } + + @Test + public void testTestFileName() { + assertEquals(sut.getTestFileName(), filename); + } + + @Test + public void testTestFileNameWithoutExtension() { + assertEquals(sut.getTestFileNameWithoutExtension(), filename); + } + + @Test + public void testProductionFilePath() { + assertEquals(sut.getProductionFilePath(), path); + } + + @Test + public void testRelativeProductionFilePath() { + assertEquals(sut.getRelativeProductionFilePath(), relativePath); + } + + @Test + public void testProductionFileName() { + assertEquals(sut.getProductionFileName(), filename); + } + + @Test + public void testProductionFileNameWithoutExtension() { + assertEquals(sut.getProductionFileNameWithoutExtension(), filename); + } } \ No newline at end of file From 4821c6297eafe9ccf18c54a39a8e0144ecef56a1 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Thu, 27 Aug 2020 16:30:34 -0300 Subject: [PATCH 09/42] On TestFile constructor, file without extension and path without separator aren't treated as special cases, they throw an IllegalArgumentException instead --- .../rit/se/testsmells/testsmell/TestFile.java | 31 +++- .../se/testsmells/testsmell/TestFileTest.java | 147 ++++++------------ .../testsmell/TestSmellDetectorTest.java | 4 + 3 files changed, 74 insertions(+), 108 deletions(-) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java index ce0e447..89e4062 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java @@ -11,11 +11,26 @@ public class TestFile extends SmellsContainer { public TestFile(String app, String testFilePath, String productionFilePath) { super(); + checkValidity(testFilePath, productionFilePath, app); this.app = app; this.testFilePath = testFilePath; this.productionFilePath = productionFilePath; } + protected void checkValidity(String testPath, String prodPath, String app) { + if (!haveExtension(testPath, prodPath) || !haveFileSeparator(testPath, prodPath) || app.isEmpty()) { + throw new IllegalArgumentException("Both testFilePath and productionFilePath should include extensions and file separator. App cannot be empty!"); + } + } + + private boolean haveFileSeparator(String testPath, String prodPath) { + return testPath.lastIndexOf(File.separator) != -1 && prodPath.lastIndexOf(File.separator) != -1; + } + + private boolean haveExtension(String testPath, String prodPath) { + return testPath.lastIndexOf('.') != -1 && prodPath.lastIndexOf('.') != -1; + } + public Map getTestDescriptionEntries() { Map descriptions = new HashMap<>(); @@ -76,11 +91,7 @@ public String getProductionFileName() { * @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 ""; + return extractRelativePathFrom(testFilePath); } /** @@ -90,10 +101,14 @@ public String getRelativeTestFilePath() { * @return the relative production file path */ public String getRelativeProductionFilePath() { - if (!StringUtils.isEmpty(productionFilePath)) { - int projectNameIndex = productionFilePath.lastIndexOf(app); + return extractRelativePathFrom(productionFilePath); + } + + private String extractRelativePathFrom(String path) { + if (!StringUtils.isEmpty(path)) { + int projectNameIndex = path.lastIndexOf(app); if (projectNameIndex == -1) return ""; - return productionFilePath.substring(projectNameIndex + app.length() + File.separator.length()); + return path.substring(projectNameIndex + app.length() + File.separator.length()); } else return ""; } } \ No newline at end of file diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/TestFileTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestFileTest.java index fdda2ea..b15fe43 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/TestFileTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestFileTest.java @@ -8,36 +8,29 @@ import java.io.File; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; -class TestFileTest { - +@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(); @@ -45,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); } @@ -93,89 +99,30 @@ public void testGetRelativeTestFilePathWindows() { } } -class FilePathWithoutSeparatorTest { +class TestFileSpecialCasesTest { String path; - TestFile sut; + String app; @BeforeEach public void setUp() { - path = "file.extension"; - sut = new TestFile("", path, path); - } - - @Test - public void testTestFilePath() { - assertEquals(sut.getTestFilePath(), path); + app = "project"; } @Test - public void testTestFileName() { - assertEquals(sut.getTestFileName(), path); - } - - @Test - public void testProductionFilePath() { - assertEquals(sut.getProductionFilePath(), path); - } - - @Test - public void testProductionFileName() { - assertEquals(sut.getProductionFileName(), path); - } -} - -class FileWithoutExtension { - String filename; - String relativePath; - String path; - TestFile sut; - - @BeforeEach - public void setUp() { - String app = "folder"; - filename = "fileWithoutExtension"; - relativePath = "to" + File.separator + filename; - path = app + File.separator + relativePath; - sut = new TestFile(app, path, path); - } - - @Test - public void testTestFilePath() { - assertEquals(sut.getTestFilePath(), path); - } - - @Test - public void testRelativeTestFilePath() { - assertEquals(sut.getRelativeTestFilePath(), relativePath); - } - - @Test - public void testTestFileName() { - assertEquals(sut.getTestFileName(), filename); - } - - @Test - public void testTestFileNameWithoutExtension() { - assertEquals(sut.getTestFileNameWithoutExtension(), filename); - } - - @Test - public void testProductionFilePath() { - assertEquals(sut.getProductionFilePath(), path); - } - - @Test - public void testRelativeProductionFilePath() { - assertEquals(sut.getRelativeProductionFilePath(), relativePath); + public void testFilePathWithoutSeparatorTest() { + path = "file.extension"; + assertThrows(IllegalArgumentException.class, () -> new TestFile(app, path, path)); } @Test - public void testProductionFileName() { - assertEquals(sut.getProductionFileName(), filename); + public void testFileWithoutExtension() { + path = app + File.separator + "to" + File.separator + "fileWithoutExtension"; + assertThrows(IllegalArgumentException.class, () -> new TestFile(app, path, path)); } @Test - public void testProductionFileNameWithoutExtension() { - assertEquals(sut.getProductionFileNameWithoutExtension(), filename); + 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/TestSmellDetectorTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java index d2862e2..40fe040 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java @@ -54,6 +54,10 @@ 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 ""; From aa1fe2def63641ae56392d124f247876a1dc7acb Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Fri, 28 Aug 2020 00:07:47 -0300 Subject: [PATCH 10/42] SmellsContainer became an interface with default methods and it's superclass of SmellyElement, TestClass and TestMethod TestSmellDetector add Smells on them (they now are compatible to ResultsWriter operations). --- pom.xml | 23 ++++++++++- .../testsmells/testsmell/SmellsContainer.java | 12 ++---- .../testsmells/testsmell/SmellyElement.java | 6 +-- .../se/testsmells/testsmell/TestClass.java | 11 ++++++ .../rit/se/testsmells/testsmell/TestFile.java | 39 +++++++++++-------- .../se/testsmells/testsmell/TestMethod.java | 11 ++++++ .../testsmell/TestSmellDetector.java | 6 +-- .../testsmell/MethodValidatorTest.java | 2 +- 8 files changed, 78 insertions(+), 32 deletions(-) diff --git a/pom.xml b/pom.xml index 879b4e7..975c7d5 100644 --- a/pom.xml +++ b/pom.xml @@ -29,6 +29,10 @@ 5.4.2 + + integration + --illegal-access=permit + org.apache.maven.plugins @@ -56,6 +60,23 @@ + + + integration + + + + org.apache.maven.plugins + maven-surefire-plugin + + integration + none + + + + + + @@ -87,5 +108,5 @@ test - + \ No newline at end of file diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java b/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java index f88bf89..5596a24 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java @@ -4,20 +4,16 @@ import java.util.List; import java.util.Map; -public abstract class SmellsContainer { - protected final List testSmells; - - public SmellsContainer() { - this.testSmells = new ArrayList<>(); - } +public interface SmellsContainer { + List testSmells = new ArrayList<>(); public abstract Map getTestDescriptionEntries(); - public void addDetectedSmell(AbstractSmell smell) { + default void addDetectedSmell(AbstractSmell smell) { testSmells.add(smell); } - public List getTestSmells() { + default List getTestSmells() { return testSmells; } } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/SmellyElement.java b/src/main/java/edu/rit/se/testsmells/testsmell/SmellyElement.java index ea9c3a9..4644019 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/SmellyElement.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/SmellyElement.java @@ -3,9 +3,9 @@ import java.util.HashMap; import java.util.Map; -public abstract class SmellyElement { - protected String name; - private Map data; +public abstract class SmellyElement implements SmellsContainer { + private final String name; + private final Map data; private boolean hasSmell; public SmellyElement(String name) { diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestClass.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestClass.java index b23c5f1..c8f1de1 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestClass.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestClass.java @@ -1,9 +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 index 89e4062..9e6f786 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java @@ -1,25 +1,33 @@ package edu.rit.se.testsmells.testsmell; -import org.apache.commons.lang3.StringUtils; - import java.io.File; import java.util.HashMap; import java.util.Map; -public class TestFile extends SmellsContainer { +public class TestFile implements SmellsContainer { private final String app, testFilePath, productionFilePath; public TestFile(String app, String testFilePath, String productionFilePath) { - super(); 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 (!haveExtension(testPath, prodPath) || !haveFileSeparator(testPath, prodPath) || app.isEmpty()) { - throw new IllegalArgumentException("Both testFilePath and productionFilePath should include extensions and file separator. App cannot be empty!"); + 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!"); } } @@ -31,6 +39,11 @@ private boolean haveExtension(String testPath, String prodPath) { return testPath.lastIndexOf('.') != -1 && 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<>(); @@ -65,11 +78,7 @@ public String getProductionFileNameWithoutExtension() { } private String removeExtension(String filename) { - try { - return filename.substring(0, filename.lastIndexOf(".")); - } catch (StringIndexOutOfBoundsException e) { - return filename; - } + return filename.substring(0, filename.lastIndexOf(".")); } private String extractFileFromPath(String path) { @@ -105,10 +114,8 @@ public String getRelativeProductionFilePath() { } private String extractRelativePathFrom(String path) { - if (!StringUtils.isEmpty(path)) { - int projectNameIndex = path.lastIndexOf(app); - if (projectNameIndex == -1) return ""; - return path.substring(projectNameIndex + app.length() + File.separator.length()); - } else return ""; + int projectNameIndex = path.lastIndexOf(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 index 71a4a7e..bad8d8d 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestMethod.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestMethod.java @@ -1,9 +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 index 807995e..a2847cb 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java @@ -56,7 +56,9 @@ public void detectSmells(TestFile testFile) throws IOException { try { smell.runAnalysis(testFileCompilationUnit, productionFileCompilationUnit, testFile.getTestFileNameWithoutExtension(), testFile.getProductionFileNameWithoutExtension()); testFile.addDetectedSmell(smell); - //TODO: Use smell.getSmellyElements() to aggregate smelly classes and methods + for (SmellsContainer element : smell.getSmellyElements()) { + element.addDetectedSmell(smell); + } } catch (FileNotFoundException ignored) { } finally { testFile.addDetectedSmell(null); @@ -73,6 +75,4 @@ private CompilationUnit parseIntoCompilationUnit(String testFilePath) throws Fil testFileInputStream = new FileInputStream(testFilePath); return JavaParser.parse(testFileInputStream); } - - } diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java index 64ca187..9f7797d 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java @@ -170,8 +170,8 @@ public MethodVisitorStub(Consumer func) { @Override public void visit(MethodDeclaration n, Void arg) { - super.visit(n, arg); func.accept(n); + super.visit(n, arg); } } } \ No newline at end of file From e2e6bd37cb6deb1c45a2c1dd8d20030c205fd26b Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Fri, 28 Aug 2020 00:09:29 -0300 Subject: [PATCH 11/42] New class extracted from Main. It will handle report granularity-level configuration. --- src/main/java/edu/rit/se/testsmells/Main.java | 15 +++----- .../ExportingGranularityController.java | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+), 11 deletions(-) create mode 100644 src/main/java/edu/rit/se/testsmells/testsmell/ExportingGranularityController.java diff --git a/src/main/java/edu/rit/se/testsmells/Main.java b/src/main/java/edu/rit/se/testsmells/Main.java index f0c7a95..df68bb1 100644 --- a/src/main/java/edu/rit/se/testsmells/Main.java +++ b/src/main/java/edu/rit/se/testsmells/Main.java @@ -1,5 +1,6 @@ package edu.rit.se.testsmells; +import edu.rit.se.testsmells.testsmell.ExportingGranularityController; import edu.rit.se.testsmells.testsmell.ResultsWriter; import edu.rit.se.testsmells.testsmell.TestFile; import edu.rit.se.testsmells.testsmell.TestSmellDetector; @@ -9,9 +10,7 @@ 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 { @@ -22,12 +21,10 @@ public static void main(String[] args) throws IOException { List files = readInputTestFiles(inputFile); ResultsWriter resultsWriter = initializeOutputFile(testSmellDetector, files.get(0)); - for (TestFile file : files) { - System.out.println(getCurrentDateFormatted() + " Processing: " + file.getTestFilePath()); + ExportingGranularityController exportCtrl = new ExportingGranularityController(testSmellDetector, resultsWriter); - testSmellDetector.detectSmells(file); - resultsWriter.exportSmells(file); - } + exportCtrl.addSmells(files); + exportCtrl.run(); System.out.println("end"); } @@ -71,10 +68,6 @@ private static File handleCliArgs(String[] args) { return inputFile; } - private static Object getCurrentDateFormatted() { - return (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()); - } - private static TestSmellDetector initializeSmells() { TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ExportingGranularityController.java b/src/main/java/edu/rit/se/testsmells/testsmell/ExportingGranularityController.java new file mode 100644 index 0000000..cf5f2b9 --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ExportingGranularityController.java @@ -0,0 +1,36 @@ +package edu.rit.se.testsmells.testsmell; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +public class ExportingGranularityController { + + private final TestSmellDetector testSmellDetector; + private final ResultsWriter resultsWriter; + private List files; + + public ExportingGranularityController(TestSmellDetector testSmellDetector, ResultsWriter resultsWriter) { + this.testSmellDetector = testSmellDetector; + this.resultsWriter = resultsWriter; + + } + + public void addSmells(List files) { + this.files = files; + } + + public void run() throws IOException { + for (TestFile file : files) { + System.out.println(getCurrentDateFormatted() + " Processing: " + file.getTestFilePath()); + + testSmellDetector.detectSmells(file); + resultsWriter.exportSmells(file); + } + } + + private Object getCurrentDateFormatted() { + return (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()); + } +} From 1825641b0448a16905239a5714fa96f37974e05a Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Fri, 28 Aug 2020 04:06:52 -0300 Subject: [PATCH 12/42] ExportingGranularityController renamed to ReportController, report granularity cfg by properties and handled by ReportController. Supported granularities: CLASS, METHOD and FILE. --- pom.xml | 22 +++++- src/main/java/edu/rit/se/testsmells/Main.java | 18 ++++- .../ExportingGranularityController.java | 36 --------- .../testsmell/ReportController.java | 74 +++++++++++++++++++ src/resources/META-INF/MANIFEST.MF | 2 +- src/resources/test-smells.properties | 1 + 6 files changed, 108 insertions(+), 45 deletions(-) delete mode 100644 src/main/java/edu/rit/se/testsmells/testsmell/ExportingGranularityController.java create mode 100644 src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java create mode 100644 src/resources/test-smells.properties diff --git a/pom.xml b/pom.xml index 975c7d5..674cd8c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,12 +3,17 @@ 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 jar + + + src/resources + true + + org.apache.maven.plugins @@ -34,6 +39,17 @@ --illegal-access=permit + + org.apache.maven.plugins + maven-jar-plugin + + + + ${project.MainClass} + + + + org.apache.maven.plugins maven-assembly-plugin @@ -46,9 +62,7 @@ - - Main - + ${project.MainClass} diff --git a/src/main/java/edu/rit/se/testsmells/Main.java b/src/main/java/edu/rit/se/testsmells/Main.java index df68bb1..3901e03 100644 --- a/src/main/java/edu/rit/se/testsmells/Main.java +++ b/src/main/java/edu/rit/se/testsmells/Main.java @@ -1,6 +1,6 @@ package edu.rit.se.testsmells; -import edu.rit.se.testsmells.testsmell.ExportingGranularityController; +import edu.rit.se.testsmells.testsmell.ReportController; import edu.rit.se.testsmells.testsmell.ResultsWriter; import edu.rit.se.testsmells.testsmell.TestFile; import edu.rit.se.testsmells.testsmell.TestSmellDetector; @@ -10,7 +10,9 @@ 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 { @@ -21,14 +23,22 @@ public static void main(String[] args) throws IOException { List files = readInputTestFiles(inputFile); ResultsWriter resultsWriter = initializeOutputFile(testSmellDetector, files.get(0)); - ExportingGranularityController exportCtrl = new ExportingGranularityController(testSmellDetector, resultsWriter); + ReportController reportCtrl = new ReportController(resultsWriter); - exportCtrl.addSmells(files); - exportCtrl.run(); + 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 ResultsWriter initializeOutputFile(TestSmellDetector testSmellDetector, TestFile anyFile) throws IOException { ResultsWriter resultsWriter = ResultsWriter.createResultsWriter(); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ExportingGranularityController.java b/src/main/java/edu/rit/se/testsmells/testsmell/ExportingGranularityController.java deleted file mode 100644 index cf5f2b9..0000000 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ExportingGranularityController.java +++ /dev/null @@ -1,36 +0,0 @@ -package edu.rit.se.testsmells.testsmell; - -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; - -public class ExportingGranularityController { - - private final TestSmellDetector testSmellDetector; - private final ResultsWriter resultsWriter; - private List files; - - public ExportingGranularityController(TestSmellDetector testSmellDetector, ResultsWriter resultsWriter) { - this.testSmellDetector = testSmellDetector; - this.resultsWriter = resultsWriter; - - } - - public void addSmells(List files) { - this.files = files; - } - - public void run() throws IOException { - for (TestFile file : files) { - System.out.println(getCurrentDateFormatted() + " Processing: " + file.getTestFilePath()); - - testSmellDetector.detectSmells(file); - resultsWriter.exportSmells(file); - } - } - - private Object getCurrentDateFormatted() { - return (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()); - } -} 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..563b59f --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java @@ -0,0 +1,74 @@ +package edu.rit.se.testsmells.testsmell; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; +import java.util.stream.Collectors; + +enum ReportGranularity {FILE, CLASS, METHOD} + +public class ReportController { + private final ResultsWriter resultsWriter; + private final List configuredGranularties; + private final String PROPERTIES_FILENAME = "test-smells.properties"; + private String PROPERTIES_KEY = "report.granularity"; + + public ReportController(ResultsWriter resultsWriter) throws IOException { + this.resultsWriter = resultsWriter; + + configuredGranularties = readProperties(); + } + + private List readProperties() throws IOException { + Properties prop = new Properties(); + prop.load(getClass().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 : configuredGranularties) { + switch (config) { + case CLASS: + reportTestClasses(files); + break; + case METHOD: + reportTestMethods(files); + break; + case FILE: + reportTestFiles(files); + break; + default: + throw new IllegalStateException("Unexpected value: " + config); + } + } + } + + private void reportTestMethods(List files) throws IOException { + for (TestFile file : files) { + reportSmellyElements(file.getTestSmells(), TestMethod.class); + } + } + + private void reportTestClasses(List files) throws IOException { + for (TestFile file : files) { + reportSmellyElements(file.getTestSmells(), TestClass.class); + } + } + + private void reportTestFiles(List files) throws IOException { + for (TestFile file : files) { + resultsWriter.exportSmells(file); + } + } + + private void reportSmellyElements(List smells, Class type) throws IOException { + for (AbstractSmell smell : smells) { + List smellyMethods = smell.getSmellyElements().stream().filter(type::isInstance).collect(Collectors.toList()); + for (SmellyElement elem : smellyMethods) { + resultsWriter.exportSmells(elem); + } + } + } +} diff --git a/src/resources/META-INF/MANIFEST.MF b/src/resources/META-INF/MANIFEST.MF index 5ee19cb..57a66e9 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 +Main-Class: edu.rit.se.testsmells.Main diff --git a/src/resources/test-smells.properties b/src/resources/test-smells.properties new file mode 100644 index 0000000..cf28140 --- /dev/null +++ b/src/resources/test-smells.properties @@ -0,0 +1 @@ +report.granularity=FILE \ No newline at end of file From c750a85dddfc679fc0d26f17a5f2e47f17cfe42b Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Fri, 28 Aug 2020 05:35:12 -0300 Subject: [PATCH 13/42] IntegrationTest implemented for TestSmellDetector.detectSmells (tagged with custom annotation and configured properly on maven). Necessary changes to TestSmellDetector to handle tested code on resources (also included on commit). --- pom.xml | 9 +++ .../testsmell/TestSmellDetector.java | 13 ++-- src/resources/META-INF/MANIFEST.MF | 6 +- .../testsmells/testsmell/IntegrationTest.java | 14 ++++ .../testsmell/TestSmellDetectorIT.java | 72 +++++++++++++++++++ 5 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/IntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIT.java diff --git a/pom.xml b/pom.xml index 674cd8c..e1110f3 100644 --- a/pom.xml +++ b/pom.xml @@ -7,6 +7,9 @@ TestSmellDetector 0.1 jar + + edu.rit.se.testsmells.Main + @@ -14,6 +17,12 @@ true + + + src/resources + true + + org.apache.maven.plugins diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java index a2847cb..93ac66e 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java @@ -7,6 +7,7 @@ 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.stream.Collectors; @@ -67,12 +68,16 @@ public void detectSmells(TestFile testFile) throws IOException { } - private CompilationUnit parseIntoCompilationUnit(String testFilePath) throws FileNotFoundException { - if (StringUtils.isEmpty(testFilePath)) { + private CompilationUnit parseIntoCompilationUnit(String filePath) { + if (StringUtils.isEmpty(filePath)) { return null; } - FileInputStream testFileInputStream; - testFileInputStream = new FileInputStream(testFilePath); + InputStream testFileInputStream; + try { + testFileInputStream = new FileInputStream(filePath); + } catch (IOException e) { + testFileInputStream = getClass().getResourceAsStream(filePath); + } return JavaParser.parse(testFileInputStream); } } diff --git a/src/resources/META-INF/MANIFEST.MF b/src/resources/META-INF/MANIFEST.MF index 57a66e9..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: edu.rit.se.testsmells.Main - +Manifest-Version: 1.0 +Main-Class: edu.rit.se.testsmells.Main + 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/TestSmellDetectorIT.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIT.java new file mode 100644 index 0000000..c2f8980 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIT.java @@ -0,0 +1,72 @@ +package edu.rit.se.testsmells.testsmell; + +import edu.rit.se.testsmells.testsmell.smell.*; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.fail; + +@IntegrationTest +public class TestSmellDetectorIT { + @Test + public void testSmellDetection() { + TestSmellDetector testSmellDetector = initializeSmells(); + + String inputFilesCSVIndex = "Queue,/Queue/src/main/java/br/ufmg/aserg/victorveloso/queue/Queue.java,/Queue/src/test/java/br/ufmg/aserg/victorveloso/queue/QueueTest.java"; + List files = readInputTestFiles(inputFilesCSVIndex); + for (TestFile file : files) { + try { + testSmellDetector.detectSmells(file); + } catch (NullPointerException | IOException e) { + fail(e.getMessage()); + } + } + } + + private List readInputTestFiles(String inputFileContent) { + TestFile testFile; + List testFiles = new ArrayList<>(); + for (List entry : splitLinesAndParseCSV(inputFileContent)) { + testFile = new TestFile(entry.get(0), entry.get(1), entry.get(2)); + testFiles.add(testFile); + } + return testFiles; + } + + private List> splitLinesAndParseCSV(String inputFileContent) { + return Arrays.stream(inputFileContent.split("\n")).map(s -> Arrays.asList(s.split(","))).collect(Collectors.toList()); + } + + private 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; + } +} From 1ee8b41d6c00d4a74a45bc26cf36fb27ea9d9225 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Sun, 30 Aug 2020 08:43:43 -0300 Subject: [PATCH 14/42] Renamed TestSmellDetectorIT.testSmellDetector to 'testSmellsFreeProject' and improved assertions (validates no smells is detected); Dropped lambda usage on MethodValidatorTest (using Command pattern instead) --- .../testsmell/MethodValidatorTest.java | 49 ++++++++++++++----- .../testsmell/TestSmellDetectorIT.java | 39 +++++++++++++-- 2 files changed, 70 insertions(+), 18 deletions(-) diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java index 9f7797d..3ae3a72 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java @@ -8,8 +8,7 @@ import java.util.function.Consumer; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; class MethodValidatorTest { MethodValidator sut; @@ -28,7 +27,7 @@ void isValidTestMethod_ignored() { "public void sampleTest(){} \n" + "} \n"; - MethodVisitorStub assertion = new MethodVisitorStub((MethodDeclaration a) -> assertFalse(sut.isValidTestMethod(a))); + MethodVisitorStub assertion = new MethodVisitorStub(new TestMethodAssertionCommand(false)::invoke); assertion.visit(JavaParser.parse(code), null); } @@ -41,7 +40,7 @@ void isValidTestMethod_private() { "private void sampleTest(){} \n" + "} \n"; - MethodVisitorStub assertion = new MethodVisitorStub((MethodDeclaration a) -> assertFalse(sut.isValidTestMethod(a))); + MethodVisitorStub assertion = new MethodVisitorStub(new TestMethodAssertionCommand(false)::invoke); assertion.visit(JavaParser.parse(code), null); } @@ -54,7 +53,7 @@ void isValidTestMethod_annotated() { "public void sampleTest(){} \n" + "} \n"; - MethodVisitorStub assertion = new MethodVisitorStub((MethodDeclaration a) -> assertTrue(sut.isValidTestMethod(a))); + MethodVisitorStub assertion = new MethodVisitorStub(new TestMethodAssertionCommand(true)::invoke); assertion.visit(JavaParser.parse(code), null); } @@ -66,7 +65,7 @@ void isValidTestMethod_startsWithTest() { "public void testSample(){} \n" + "} \n"; - MethodVisitorStub assertion = new MethodVisitorStub((MethodDeclaration a) -> assertTrue(sut.isValidTestMethod(a))); + MethodVisitorStub assertion = new MethodVisitorStub(new TestMethodAssertionCommand(true)::invoke); assertion.visit(JavaParser.parse(code), null); } @@ -78,7 +77,7 @@ void isValidTestMethod_visibleNonTestMethod() { "public void sampleTest(){} \n" + "} \n"; - MethodVisitorStub assertion = new MethodVisitorStub((MethodDeclaration a) -> assertFalse(sut.isValidTestMethod(a))); + MethodVisitorStub assertion = new MethodVisitorStub(new TestMethodAssertionCommand(false)::invoke); assertion.visit(JavaParser.parse(code), null); } @@ -92,7 +91,7 @@ void isValidSetupMethod_ignored() { "public void setUp(){} \n" + "} \n"; - MethodVisitorStub assertion = new MethodVisitorStub((MethodDeclaration a) -> assertFalse(sut.isValidSetupMethod(a))); + MethodVisitorStub assertion = new MethodVisitorStub(new SetupMethodAssertionCommand(false)::invoke); assertion.visit(JavaParser.parse(code), null); } @@ -105,7 +104,7 @@ void isValidSetupMethod_private() { "private void setUp(){} \n" + "} \n"; - MethodVisitorStub assertion = new MethodVisitorStub((MethodDeclaration a) -> assertFalse(sut.isValidSetupMethod(a))); + MethodVisitorStub assertion = new MethodVisitorStub(new SetupMethodAssertionCommand(false)::invoke); assertion.visit(JavaParser.parse(code), null); } @@ -118,7 +117,7 @@ void isValidSetupMethod_annotated() { "public void setSut(){} \n" + "} \n"; - MethodVisitorStub assertion = new MethodVisitorStub((MethodDeclaration a) -> assertTrue(sut.isValidSetupMethod(a))); + MethodVisitorStub assertion = new MethodVisitorStub(new SetupMethodAssertionCommand(true)::invoke); assertion.visit(JavaParser.parse(code), null); } @@ -131,7 +130,7 @@ void isValidSetupMethod_annotatedAlt() { "public void setSut(){} \n" + "} \n"; - MethodVisitorStub assertion = new MethodVisitorStub((MethodDeclaration a) -> assertTrue(sut.isValidSetupMethod(a))); + MethodVisitorStub assertion = new MethodVisitorStub(new SetupMethodAssertionCommand(true)::invoke); assertion.visit(JavaParser.parse(code), null); } @@ -143,7 +142,7 @@ void isValidSetupMethod_namedSetUp() { "public void setUp(){} \n" + "} \n"; - MethodVisitorStub assertion = new MethodVisitorStub((MethodDeclaration a) -> assertTrue(sut.isValidSetupMethod(a))); + MethodVisitorStub assertion = new MethodVisitorStub(new SetupMethodAssertionCommand(true)::invoke); assertion.visit(JavaParser.parse(code), null); } @@ -156,7 +155,7 @@ void isValidSetupMethod_visibleButIsTestMethod() { "public void sampleTest(){} \n" + "} \n"; - MethodVisitorStub assertion = new MethodVisitorStub((MethodDeclaration a) -> assertFalse(sut.isValidSetupMethod(a))); + MethodVisitorStub assertion = new MethodVisitorStub(new SetupMethodAssertionCommand(false)::invoke); assertion.visit(JavaParser.parse(code), null); } @@ -174,4 +173,28 @@ public void visit(MethodDeclaration n, Void arg) { 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/TestSmellDetectorIT.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIT.java index c2f8980..ca17275 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIT.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIT.java @@ -1,6 +1,7 @@ package edu.rit.se.testsmells.testsmell; import edu.rit.se.testsmells.testsmell.smell.*; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -9,16 +10,39 @@ import java.util.List; import java.util.stream.Collectors; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.fail; @IntegrationTest public class TestSmellDetectorIT { + + private TestSmellDetector testSmellDetector; + private List files; + + @BeforeEach + void setUp() { + testSmellDetector = initializeSmells(); + } + @Test - public void testSmellDetection() { - TestSmellDetector testSmellDetector = initializeSmells(); + public void testSmellsFreeProject() { + detectSmells( + "SmellsFreeProject", + "/Queue/src/main/java/br/ufmg/aserg/victorveloso/queue/Queue.java", + "/Queue/src/test/java/br/ufmg/aserg/victorveloso/queue/QueueTest.java" + ); + + for (TestFile file : files) { + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + assertFalse(testSmell.hasSmell()); + } + } + } + } - String inputFilesCSVIndex = "Queue,/Queue/src/main/java/br/ufmg/aserg/victorveloso/queue/Queue.java,/Queue/src/test/java/br/ufmg/aserg/victorveloso/queue/QueueTest.java"; - List files = readInputTestFiles(inputFilesCSVIndex); + private void detectSmells(String app, String productionFile, String testFile) { + files = readInputTestFiles(formatInputValue(app, productionFile, testFile)); for (TestFile file : files) { try { testSmellDetector.detectSmells(file); @@ -28,6 +52,10 @@ public void testSmellDetection() { } } + private String formatInputValue(String app, String productionFile, String testFile) { + return app + "," + productionFile + "," + testFile; + } + private List readInputTestFiles(String inputFileContent) { TestFile testFile; List testFiles = new ArrayList<>(); @@ -47,7 +75,8 @@ private TestSmellDetector initializeSmells() { testSmellDetector.addDetectableSmell(new AssertionRoulette()); testSmellDetector.addDetectableSmell(new ConditionalTestLogic()); - testSmellDetector.addDetectableSmell(new ConstructorInitialization()); + + //testSmellDetector.addDetectableSmell(new ConstructorInitialization()); // Constructor Initialization gives false positives testSmellDetector.addDetectableSmell(new DefaultTest()); testSmellDetector.addDetectableSmell(new EmptyTest()); testSmellDetector.addDetectableSmell(new ExceptionCatchingThrowing()); From 871a2844f357a8b3bd4a79545728413bf8e2141c Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Fri, 18 Sep 2020 04:18:13 -0300 Subject: [PATCH 15/42] Optimized AbstractSmell.hasSmell, made prodFile optional, merging same type smells on SmellsContainer.addDetectedSmell --- pom.xml | 6 ++ .../testsmells/testsmell/AbstractSmell.java | 4 +- .../testsmells/testsmell/SmellsContainer.java | 25 ++++++- .../rit/se/testsmells/testsmell/TestFile.java | 14 +++- .../testsmell/TestSmellDetector.java | 2 +- .../madgag/agit/AssertionRouletteTest.java | 16 ++++ .../testsmell/TestSmellDetectorIT.java | 73 +++++++++---------- .../testsmell/TestSmellDetectorTest.java | 9 +-- 8 files changed, 96 insertions(+), 53 deletions(-) create mode 100644 src/resources/AssertionRoulette/src/test/com/madgag/agit/AssertionRouletteTest.java diff --git a/pom.xml b/pom.xml index e1110f3..0e1b7a8 100644 --- a/pom.xml +++ b/pom.xml @@ -124,6 +124,12 @@ 5.4.2 test + + org.mockito + mockito-core + 3.5.10 + test + org.junit.jupiter junit-jupiter-engine diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java index c0591d9..18b79b5 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java @@ -16,7 +16,7 @@ public abstract class AbstractSmell { public abstract void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException; public AbstractSmell() { - methodValidator = new MethodValidator(); + methodValidator = new MethodValidator(); //TODO: dependency should be injected (or, at least, be a singleton) smellyElementList = new ArrayList<>(); } @@ -35,7 +35,7 @@ public void addSmellyElement(SmellyElement elem) { * Returns true if any of the elements has a smell */ public boolean hasSmell() { - return smellyElementList.stream().filter(x -> x.hasSmell()).count() >= 1; + return smellyElementList.stream().anyMatch(SmellyElement::hasSmell); } protected boolean isNumber(String str) { diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java b/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java index 5596a24..0a0fab5 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java @@ -3,14 +3,33 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; public interface SmellsContainer { List testSmells = new ArrayList<>(); - public abstract Map getTestDescriptionEntries(); + Map getTestDescriptionEntries(); - default void addDetectedSmell(AbstractSmell smell) { - testSmells.add(smell); + default void addDetectedSmell(AbstractSmell newSmell) { + if (Objects.nonNull(newSmell)) { + AbstractSmell existingSmell = findSameTypeSmell(newSmell); + if (Objects.nonNull(existingSmell)) { + for (SmellyElement smellyElement : newSmell.getSmellyElements()) { + existingSmell.addSmellyElement(smellyElement); + } + return; + } + } + testSmells.add(newSmell); + } + + default AbstractSmell findSameTypeSmell(AbstractSmell smellType) { + for (AbstractSmell existingSmell : testSmells) { + if (Objects.nonNull(existingSmell) && smellType.getClass().equals(existingSmell.getClass())) { + return existingSmell; + } + } + return null; } default List getTestSmells() { diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java index 9e6f786..f7ca26b 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java @@ -6,6 +6,7 @@ public class TestFile implements SmellsContainer { private final String app, testFilePath, productionFilePath; + private boolean isProductionFileOmitted = false; public TestFile(String app, String testFilePath, String productionFilePath) { checkValidity(testFilePath, productionFilePath, app); @@ -23,6 +24,9 @@ public TestFile(String app, String testFilePath, String productionFilePath) { * @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."); } @@ -32,11 +36,11 @@ protected void checkValidity(String testPath, String prodPath, String app) { } private boolean haveFileSeparator(String testPath, String prodPath) { - return testPath.lastIndexOf(File.separator) != -1 && prodPath.lastIndexOf(File.separator) != -1; + return testPath.lastIndexOf(File.separator) != -1 && (isProductionFileOmitted || prodPath.lastIndexOf(File.separator) != -1); } private boolean haveExtension(String testPath, String prodPath) { - return testPath.lastIndexOf('.') != -1 && prodPath.lastIndexOf('.') != -1; + return testPath.lastIndexOf('.') != -1 && (isProductionFileOmitted || prodPath.lastIndexOf('.') != -1); } /** @@ -74,6 +78,9 @@ public String getTestFileNameWithoutExtension() { } public String getProductionFileNameWithoutExtension() { + if (isProductionFileOmitted) { + return ""; + } return removeExtension(getProductionFileName()); } @@ -90,6 +97,9 @@ public String getTestFileName() { } public String getProductionFileName() { + if (isProductionFileOmitted) { + return ""; + } return extractFileFromPath(productionFilePath); } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java index 93ac66e..bc4b015 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java @@ -48,7 +48,7 @@ public List getTestSmellNames() { /** * 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) throws IOException { + public void detectSmells(TestFile testFile) { CompilationUnit testFileCompilationUnit = parseIntoCompilationUnit(testFile.getTestFilePath()); CompilationUnit productionFileCompilationUnit = parseIntoCompilationUnit(testFile.getProductionFilePath()); diff --git a/src/resources/AssertionRoulette/src/test/com/madgag/agit/AssertionRouletteTest.java b/src/resources/AssertionRoulette/src/test/com/madgag/agit/AssertionRouletteTest.java new file mode 100644 index 0000000..f4b45f9 --- /dev/null +++ b/src/resources/AssertionRoulette/src/test/com/madgag/agit/AssertionRouletteTest.java @@ -0,0 +1,16 @@ +package com.madgag.agit; + +public class AssertionRouletteTest { + @Test + 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)); + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIT.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIT.java index ca17275..9073d55 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIT.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIT.java @@ -4,20 +4,16 @@ 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.Collections; import java.util.List; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertTrue; @IntegrationTest public class TestSmellDetectorIT { private TestSmellDetector testSmellDetector; - private List files; @BeforeEach void setUp() { @@ -25,49 +21,50 @@ void setUp() { } @Test - public void testSmellsFreeProject() { - detectSmells( - "SmellsFreeProject", - "/Queue/src/main/java/br/ufmg/aserg/victorveloso/queue/Queue.java", - "/Queue/src/test/java/br/ufmg/aserg/victorveloso/queue/QueueTest.java" - ); - + public void testAssertionRoulette() { + List files = Collections.singletonList(new TestFile( + "AssertionRouletteProject", + "/AssertionRoulette/src/test/com/madgag/agit/AssertionRouletteTest.java", + "" + )); + + detectSmells(files); + boolean expectedSmellDetected = false; for (TestFile file : files) { for (AbstractSmell testSmell : file.getTestSmells()) { if (testSmell != null) { - assertFalse(testSmell.hasSmell()); + if (testSmell.getSmellName().equals(new AssertionRoulette().getSmellName())) { + expectedSmellDetected = testSmell.hasSmell(); + } } } } + assertTrue(expectedSmellDetected); + } - private void detectSmells(String app, String productionFile, String testFile) { - files = readInputTestFiles(formatInputValue(app, productionFile, testFile)); + @Test + public void testSmellsFreeProject() { + List files = Collections.singletonList(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" + )); + detectSmells(files); + for (TestFile file : files) { - try { - testSmellDetector.detectSmells(file); - } catch (NullPointerException | IOException e) { - fail(e.getMessage()); + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + assertFalse(testSmell.hasSmell(), String.format("Detected smell named %s", testSmell.getSmellName())); + } } } } - private String formatInputValue(String app, String productionFile, String testFile) { - return app + "," + productionFile + "," + testFile; - } - - private List readInputTestFiles(String inputFileContent) { - TestFile testFile; - List testFiles = new ArrayList<>(); - for (List entry : splitLinesAndParseCSV(inputFileContent)) { - testFile = new TestFile(entry.get(0), entry.get(1), entry.get(2)); - testFiles.add(testFile); + private void detectSmells(List files) { + for (TestFile file : files) { + testSmellDetector.detectSmells(file); } - return testFiles; - } - - private List> splitLinesAndParseCSV(String inputFileContent) { - return Arrays.stream(inputFileContent.split("\n")).map(s -> Arrays.asList(s.split(","))).collect(Collectors.toList()); } private TestSmellDetector initializeSmells() { @@ -76,14 +73,14 @@ private TestSmellDetector initializeSmells() { testSmellDetector.addDetectableSmell(new AssertionRoulette()); testSmellDetector.addDetectableSmell(new ConditionalTestLogic()); - //testSmellDetector.addDetectableSmell(new ConstructorInitialization()); // Constructor Initialization gives false positives + testSmellDetector.addDetectableSmell(new ConstructorInitialization()); // Constructor Initialization gives false positives 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 MysteryGuest()); testSmellDetector.addDetectableSmell(new SensitiveEquality()); testSmellDetector.addDetectableSmell(new VerboseTest()); testSmellDetector.addDetectableSmell(new SleepyTest()); @@ -91,7 +88,7 @@ private TestSmellDetector initializeSmells() { testSmellDetector.addDetectableSmell(new LazyTest()); testSmellDetector.addDetectableSmell(new DuplicateAssert()); testSmellDetector.addDetectableSmell(new UnknownTest()); - testSmellDetector.addDetectableSmell(new IgnoredTest()); + //testSmellDetector.addDetectableSmell(new IgnoredTest()); testSmellDetector.addDetectableSmell(new ResourceOptimism()); testSmellDetector.addDetectableSmell(new MagicNumberTest()); testSmellDetector.addDetectableSmell(new DependentTest()); diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java index 40fe040..89268c2 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java @@ -6,7 +6,6 @@ import org.junit.jupiter.api.Test; import java.io.FileNotFoundException; -import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -38,12 +37,8 @@ void detectSmells() { TestFile tf = new TestFileStub("app", "", ""); List smells = new ArrayList<>(); - try { - this.sut.detectSmells(tf); - smells = tf.getTestSmells(); - } catch (IOException e) { - fail("Exception IOException unexpectedly raised: " + e.getMessage()); - } + this.sut.detectSmells(tf); + smells = tf.getTestSmells(); assertEquals(smells.size(), 1); assertNull(smells.get(0)); From 81b1eaa4530397206535bbc5b0d7d1e341499567 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Fri, 18 Sep 2020 04:22:42 -0300 Subject: [PATCH 16/42] Breaking change: no null should be added as testFile's smells on TestSmellDetector.detectSmells (it prevents duplicate smells removal on smells merging) --- .../testsmells/testsmell/SmellsContainer.java | 19 +++++++++---------- .../testsmell/TestSmellDetector.java | 11 +++++------ .../testsmell/TestSmellDetectorTest.java | 2 +- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java b/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java index 0a0fab5..11a5b7d 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java @@ -11,21 +11,20 @@ public interface SmellsContainer { Map getTestDescriptionEntries(); default void addDetectedSmell(AbstractSmell newSmell) { - if (Objects.nonNull(newSmell)) { - AbstractSmell existingSmell = findSameTypeSmell(newSmell); - if (Objects.nonNull(existingSmell)) { - for (SmellyElement smellyElement : newSmell.getSmellyElements()) { - existingSmell.addSmellyElement(smellyElement); - } - return; + assert Objects.nonNull(newSmell); + AbstractSmell existingSmell = findSmellNamed(newSmell.getSmellName()); + if (Objects.nonNull(existingSmell)) { + for (SmellyElement smellyElement : newSmell.getSmellyElements()) { + existingSmell.addSmellyElement(smellyElement); } + } else { + testSmells.add(newSmell); } - testSmells.add(newSmell); } - default AbstractSmell findSameTypeSmell(AbstractSmell smellType) { + default AbstractSmell findSmellNamed(String smellName) { for (AbstractSmell existingSmell : testSmells) { - if (Objects.nonNull(existingSmell) && smellType.getClass().equals(existingSmell.getClass())) { + if (smellName.equals(existingSmell.getSmellName())) { return existingSmell; } } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java index bc4b015..399975d 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java @@ -56,13 +56,12 @@ public void detectSmells(TestFile testFile) { for (AbstractSmell smell : testSmells) { try { smell.runAnalysis(testFileCompilationUnit, productionFileCompilationUnit, testFile.getTestFileNameWithoutExtension(), testFile.getProductionFileNameWithoutExtension()); - testFile.addDetectedSmell(smell); - for (SmellsContainer element : smell.getSmellyElements()) { - element.addDetectedSmell(smell); - } } catch (FileNotFoundException ignored) { - } finally { - testFile.addDetectedSmell(null); + } + + testFile.addDetectedSmell(smell); + for (SmellsContainer element : smell.getSmellyElements()) { + element.addDetectedSmell(smell); } } diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java index 89268c2..407caff 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java @@ -41,7 +41,7 @@ void detectSmells() { smells = tf.getTestSmells(); assertEquals(smells.size(), 1); - assertNull(smells.get(0)); + assertFalse(smells.get(0).hasSmell()); } private static class TestFileStub extends TestFile { From ff64c7df5c2ad6d8de9e466a18b61f8c2ed05fa1 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Fri, 18 Sep 2020 19:06:11 -0300 Subject: [PATCH 17/42] Tests implemented for each TestSmell (production file specified only when required). IgnoredTest results in false positive; ConstructorInitialization and GeneralFixture are not detected. --- .../testsmells/testsmell/AbstractSmell.java | 15 +- .../testsmells/testsmell/SmellsContainer.java | 4 +- .../testsmells/testsmell/SmellyElement.java | 4 + .../testsmell/TestSmellDetector.java | 26 +- .../com/madgag/agit/GitAsyncTaskTest.java | 293 +++++ .../madgag/agit/AssertionRouletteTest.java | 16 - .../com/dalthed/tucan/EventsScraperTest.java | 52 + .../bramble/crypto/TagEncodingTest.java | 59 + .../ExampleUnitTest.java | 676 ++++++++++ .../org/openbmap/utils/XmlSanitizerTest.java | 41 + .../gpslogger/loggers/nmea/NmeaSentence.java | 89 ++ .../loggers/nmea/NmeaSentenceTest.java | 85 ++ .../clipcaster/parser/LastPassParserTest.java | 249 ++++ .../test/calculation/AbrissTest.java | 573 +++++++++ .../cadroid/test/CertificateInfoTest.java | 69 + .../java/org/bitcoinj/core/PeerGroupTest.java | 856 +++++++++++++ .../github/marmaladesky/Cryptographer.java | 157 +++ .../marmaladesky/tests/CryptographerTest.java | 51 + .../calculator/SolarEventCalculatorTest.java | 72 ++ .../johan/batterylogger/SystemStateTest.java | 276 ++++ .../litmus/worldscope/LoginActivityTest.java | 41 + .../control/util/Space3DTransformerTest.java | 91 ++ .../gadgetbridge/test/LoggingTest.java | 110 ++ .../test/java/org/ethereum/util/RLPTest.java | 1136 +++++++++++++++++ .../sk/baka/aedict/ResultActivityTest.java | 301 +++++ .../RetrofitApiClientIntegrationTest.java | 191 +++ ... => TestSmellDetectorIntegrationTest.java} | 56 +- .../testsmell/TestSmellDetectorTest.java | 3 +- .../AssertionRouletteIntegrationTest.java | 53 + .../ConditionalTestLogicIntegrationTest.java | 54 + ...structorInitializationIntegrationTest.java | 54 + .../smell/DefaultTestIntegrationTest.java | 54 + .../smell/DuplicateAssertIntegrationTest.java | 53 + .../smell/EagerTestIntegrationTest.java | 54 + .../smell/EmptyTestIntegrationTest.java | 54 + .../ExceptionHandlingIntegrationTest.java | 54 + .../smell/GeneralFixtureIntegrationTest.java | 55 + .../smell/IgnoredTestIntegrationTest.java | 55 + .../smell/LazyTestIntegrationTest.java | 56 + .../smell/MagicNumberTestIntegrationTest.java | 53 + .../smell/MysteryGuestIntegrationTest.java | 53 + .../RedundantAssertionIntegrationTest.java | 53 + .../smell/RedundantPrintIntegrationTest.java | 53 + .../ResourceOptimismIntegrationTest.java | 54 + .../SensitiveEqualityIntegrationTest.java | 54 + .../smell/SleepyTestIntegrationTest.java | 54 + .../smell/UnknownTestIntegrationTest.java | 54 + 47 files changed, 6549 insertions(+), 67 deletions(-) create mode 100644 src/resources/AssertionRoulette/agit-integration-tests/src/main/java/com/madgag/agit/GitAsyncTaskTest.java delete mode 100644 src/resources/AssertionRoulette/src/test/com/madgag/agit/AssertionRouletteTest.java create mode 100644 src/resources/ConditionalTestLogic/src/test/java/com/dalthed/tucan/EventsScraperTest.java create mode 100644 src/resources/ConstructorInitialization/src/test/java/org/briarproject/bramble/crypto/TagEncodingTest.java create mode 100644 src/resources/DefaultTest/src/test/java/com/app/missednotificationsreminder/ExampleUnitTest.java create mode 100644 src/resources/DuplicateAssert/src/test/java/org/openbmap/utils/XmlSanitizerTest.java create mode 100644 src/resources/EagerTest/src/main/java/com/mendhak/gpslogger/loggers/nmea/NmeaSentence.java create mode 100644 src/resources/EagerTest/src/test/java/com/mendhak/gpslogger/loggers/nmea/NmeaSentenceTest.java create mode 100644 src/resources/EmptyTest/src/androidTest/java/com/actisec/clipcaster/parser/LastPassParserTest.java create mode 100644 src/resources/ExceptionHandling/src/androidTest/java/ch/hgdev/toposuite/test/calculation/AbrissTest.java create mode 100644 src/resources/GeneralFixture/src/androidTest/java/at/bitfire/cadroid/test/CertificateInfoTest.java create mode 100644 src/resources/IgnoredTest/src/test/java/org/bitcoinj/core/PeerGroupTest.java create mode 100644 src/resources/LazyTest/src/main/java/com/github/marmaladesky/Cryptographer.java create mode 100644 src/resources/LazyTest/src/test/java/com/github/marmaladesky/tests/CryptographerTest.java create mode 100644 src/resources/MagicNumberTest/src/test/java/com/luckycatlabs/sunrisesunset/calculator/SolarEventCalculatorTest.java create mode 100644 src/resources/MysteryGuest/src/androidTest/java/com/gmail/walles/johan/batterylogger/SystemStateTest.java create mode 100644 src/resources/RedundantAssertion/src/androidTest/java/com/litmus/worldscope/LoginActivityTest.java create mode 100644 src/resources/RedundantPrint/src/test/java/org/hwyl/sexytopo/control/util/Space3DTransformerTest.java create mode 100644 src/resources/ResourceOptimism/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LoggingTest.java create mode 100644 src/resources/SensitiveEquality/src/test/java/org/ethereum/util/RLPTest.java create mode 100644 src/resources/SleepyTest/src/sk/baka/aedict/ResultActivityTest.java create mode 100644 src/resources/UnknownTest/src/test/java/net/cyclestreets/api/client/RetrofitApiClientIntegrationTest.java rename src/test/java/edu/rit/se/testsmells/testsmell/{TestSmellDetectorIT.java => TestSmellDetectorIntegrationTest.java} (58%) create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/AssertionRouletteIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogicIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitializationIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/DefaultTestIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssertIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/EagerTestIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/EmptyTestIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/ExceptionHandlingIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixtureIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTestIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/LazyTestIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTestIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuestIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertionIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/RedundantPrintIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimismIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEqualityIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/SleepyTestIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/smell/UnknownTestIntegrationTest.java diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java index 18b79b5..d44e6ea 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java @@ -4,8 +4,8 @@ import com.github.javaparser.ast.body.MethodDeclaration; import java.io.FileNotFoundException; -import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; public abstract class AbstractSmell { private final MethodValidator methodValidator; @@ -13,11 +13,18 @@ public abstract class AbstractSmell { public abstract String getSmellName(); - public abstract void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException; - public AbstractSmell() { methodValidator = new MethodValidator(); //TODO: dependency should be injected (or, at least, be a singleton) - smellyElementList = new ArrayList<>(); + smellyElementList = new CopyOnWriteArrayList<>(); + } + + 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(); } /** diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java b/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java index 11a5b7d..39ed95e 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java @@ -1,12 +1,12 @@ package edu.rit.se.testsmells.testsmell; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; public interface SmellsContainer { - List testSmells = new ArrayList<>(); + List testSmells = new CopyOnWriteArrayList<>(); Map getTestDescriptionEntries(); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/SmellyElement.java b/src/main/java/edu/rit/se/testsmells/testsmell/SmellyElement.java index 4644019..2279d63 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/SmellyElement.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/SmellyElement.java @@ -13,6 +13,10 @@ public SmellyElement(String name) { this.name = name; } + public void clear() { + data.clear(); + } + public void setHasSmell(boolean hasSmell) { this.hasSmell = hasSmell; } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java index 399975d..0f8fe11 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java @@ -10,10 +10,12 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; public class TestSmellDetector { + private List inputStreams; private List testSmells; /** @@ -21,12 +23,27 @@ public class TestSmellDetector { */ 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 * @@ -48,7 +65,7 @@ public List getTestSmellNames() { /** * 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) { + public void detectSmells(TestFile testFile) throws IOException { CompilationUnit testFileCompilationUnit = parseIntoCompilationUnit(testFile.getTestFilePath()); CompilationUnit productionFileCompilationUnit = parseIntoCompilationUnit(testFile.getProductionFilePath()); @@ -58,7 +75,7 @@ public void detectSmells(TestFile testFile) { smell.runAnalysis(testFileCompilationUnit, productionFileCompilationUnit, testFile.getTestFileNameWithoutExtension(), testFile.getProductionFileNameWithoutExtension()); } catch (FileNotFoundException ignored) { } - + testFile.addDetectedSmell(smell); for (SmellsContainer element : smell.getSmellyElements()) { element.addDetectedSmell(smell); @@ -71,12 +88,15 @@ private CompilationUnit parseIntoCompilationUnit(String filePath) { if (StringUtils.isEmpty(filePath)) { return null; } - InputStream testFileInputStream; + 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/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/AssertionRoulette/src/test/com/madgag/agit/AssertionRouletteTest.java b/src/resources/AssertionRoulette/src/test/com/madgag/agit/AssertionRouletteTest.java deleted file mode 100644 index f4b45f9..0000000 --- a/src/resources/AssertionRoulette/src/test/com/madgag/agit/AssertionRouletteTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.madgag.agit; - -public class AssertionRouletteTest { - @Test - 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)); - } -} 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/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/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/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIT.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIntegrationTest.java similarity index 58% rename from src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIT.java rename to src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIntegrationTest.java index 9073d55..02be5f4 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIT.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIntegrationTest.java @@ -1,17 +1,16 @@ 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.util.Collections; -import java.util.List; +import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; @IntegrationTest -public class TestSmellDetectorIT { +public class TestSmellDetectorIntegrationTest { private TestSmellDetector testSmellDetector; @@ -20,53 +19,28 @@ void setUp() { testSmellDetector = initializeSmells(); } - @Test - public void testAssertionRoulette() { - List files = Collections.singletonList(new TestFile( - "AssertionRouletteProject", - "/AssertionRoulette/src/test/com/madgag/agit/AssertionRouletteTest.java", - "" - )); - - detectSmells(files); - boolean expectedSmellDetected = false; - for (TestFile file : files) { - for (AbstractSmell testSmell : file.getTestSmells()) { - if (testSmell != null) { - if (testSmell.getSmellName().equals(new AssertionRoulette().getSmellName())) { - expectedSmellDetected = testSmell.hasSmell(); - } - } - } - } - assertTrue(expectedSmellDetected); - + @AfterEach + void tearDown() { + testSmellDetector.clear(); + testSmellDetector = null; } @Test - public void testSmellsFreeProject() { - List files = Collections.singletonList(new TestFile( + 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" - )); - detectSmells(files); + ); + testSmellDetector.detectSmells(file); - for (TestFile file : files) { - for (AbstractSmell testSmell : file.getTestSmells()) { - if (testSmell != null) { - assertFalse(testSmell.hasSmell(), String.format("Detected smell named %s", testSmell.getSmellName())); - } + for (AbstractSmell testSmell : file.getTestSmells()) { + if (testSmell != null) { + assertFalse(testSmell.hasSmell(), String.format("Detected smell named %s", testSmell.getSmellName())); } } } - private void detectSmells(List files) { - for (TestFile file : files) { - testSmellDetector.detectSmells(file); - } - } - private TestSmellDetector initializeSmells() { TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); @@ -88,7 +62,7 @@ private TestSmellDetector initializeSmells() { testSmellDetector.addDetectableSmell(new LazyTest()); testSmellDetector.addDetectableSmell(new DuplicateAssert()); testSmellDetector.addDetectableSmell(new UnknownTest()); - //testSmellDetector.addDetectableSmell(new IgnoredTest()); + testSmellDetector.addDetectableSmell(new IgnoredTest()); testSmellDetector.addDetectableSmell(new ResourceOptimism()); testSmellDetector.addDetectableSmell(new MagicNumberTest()); testSmellDetector.addDetectableSmell(new DependentTest()); diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java index 407caff..6545f78 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test; import java.io.FileNotFoundException; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -33,7 +34,7 @@ void getTestSmellNames() { } @Test - void detectSmells() { + void detectSmells() throws IOException { TestFile tf = new TestFileStub("app", "", ""); List smells = new ArrayList<>(); 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..3e57acd --- /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 ConditionalTestLogic().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); + + } +} From d24a6eb233e347b71727ea210f59e552b5c98d10 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Fri, 25 Sep 2020 03:03:06 -0300 Subject: [PATCH 18/42] ReportController new constructor receive granularities list, relative path extraction based on first occurrence instead of last (fixing Queue test-case), ReportController tests partially implemented (FILE granularity is working) --- .../testsmell/ReportController.java | 11 +- .../testsmells/testsmell/ResultsWriter.java | 4 + .../rit/se/testsmells/testsmell/TestFile.java | 3 +- .../ReportControllerIntegrationTest.java | 142 ++++++++++++++++++ .../testsmell/ReportControllerTest.java | 63 ++++++++ .../TestSmellDetectorIntegrationTest.java | 2 + 6 files changed, 222 insertions(+), 3 deletions(-) create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java index 563b59f..76a1b9d 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java @@ -20,6 +20,12 @@ public ReportController(ResultsWriter resultsWriter) throws IOException { configuredGranularties = readProperties(); } + public ReportController(ResultsWriter resultsWriter, List granularities) { + this.resultsWriter = resultsWriter; + + configuredGranularties = granularities; + } + private List readProperties() throws IOException { Properties prop = new Properties(); prop.load(getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILENAME)); @@ -65,8 +71,9 @@ private void reportTestFiles(List files) throws IOException { private void reportSmellyElements(List smells, Class type) throws IOException { for (AbstractSmell smell : smells) { - List smellyMethods = smell.getSmellyElements().stream().filter(type::isInstance).collect(Collectors.toList()); - for (SmellyElement elem : smellyMethods) { + List smellyElements = smell.getSmellyElements(); + smellyElements = smellyElements.stream().filter(type::isInstance).collect(Collectors.toList()); + for (SmellyElement elem : smellyElements) { resultsWriter.exportSmells(elem); } } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java index 4e26f75..cfd1fb8 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java @@ -26,6 +26,10 @@ private ResultsWriter() throws IOException { writer = new FileWriter(outputFile, false); } + public String getOutputFile() { + return outputFile; + } + /** * Factory method that provides a new instance of the ResultsWriter * diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java index f7ca26b..ac90ec0 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java @@ -55,6 +55,7 @@ public Map getTestDescriptionEntries() { descriptions.put("TestFileName", getTestFileName()); descriptions.put("TestFilePath", getTestFilePath()); descriptions.put("ProductionFilePath", getProductionFilePath()); + descriptions.put("ProductionFileName", getProductionFileName()); descriptions.put("RelativeTestFilePath", getRelativeTestFilePath()); descriptions.put("RelativeProductionFilePath", getRelativeProductionFilePath()); @@ -124,7 +125,7 @@ public String getRelativeProductionFilePath() { } private String extractRelativePathFrom(String path) { - int projectNameIndex = path.lastIndexOf(app); + int projectNameIndex = path.indexOf(app); if (projectNameIndex == -1) return ""; return path.substring(projectNameIndex + app.length() + File.separator.length()); } diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java new file mode 100644 index 0000000..21363a5 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java @@ -0,0 +1,142 @@ +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.List; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.*; + +@IntegrationTest +public class ReportControllerIntegrationTest { + File outputFile; + TestFile file; + ReportController sut; + ResultsWriter resultsWriter; + List expectedSmells = Arrays.asList("Lazy Test", "Eager Test", "Exception Catching Throwing"); + 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() + ); + String appName = "LazyTest"; + String testFilePath = "/LazyTest/src/test/java/com/github/marmaladesky/tests/CryptographerTest.java"; + String productionFilePath = "/LazyTest/src/main/java/com/github/marmaladesky/Cryptographer.java"; + + @BeforeEach + public void setUp() throws IOException { + TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); + smells.forEach(testSmellDetector::addDetectableSmell); + file = new TestFile( + appName, + testFilePath, + productionFilePath + ); + resultsWriter = ResultsWriter.createResultsWriter(); + resultsWriter.writeCSVHeader(testSmellDetector, file); + testSmellDetector.detectSmells(file); + outputFile = new File(resultsWriter.getOutputFile()); + } + + @AfterEach + public void tearDown() { + outputFile.delete(); + } + + @Test + public void testReport() throws IOException { + sut = new ReportController(resultsWriter, Arrays.asList(ReportGranularity.FILE)); + assertDoesNotThrow(() -> sut.report(Arrays.asList(file))); + } + + @Test + public void testHeader() throws IOException { + sut = new ReportController(resultsWriter, Arrays.asList(ReportGranularity.FILE)); + sut.report(Arrays.asList(file)); + assertTrue(outputFile.exists(), "Output file missing!"); + + 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 + public void testContent_FILE() throws IOException { + sut = new ReportController(resultsWriter, Arrays.asList(ReportGranularity.FILE)); + sut.report(Arrays.asList(file)); + assertTrue(outputFile.exists(), "Output file missing!"); + + List contentEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).lines().skip(1).findFirst().get().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 hasSmell = smells.stream().map(x -> expectedSmells.contains(x.getSmellName()) ? "true" : "false").collect(Collectors.toList()); + + expectedEntries.addAll(hasSmell); + + assertIterableEquals(expectedEntries, contentEntries); + } + + @Test + public void testContent_METHOD() throws IOException { + sut = new ReportController(resultsWriter, Arrays.asList(ReportGranularity.METHOD)); + sut.report(Arrays.asList(file)); + assertTrue(outputFile.exists(), "Output file missing!"); + + assertEquals("", new BufferedReader(new FileReader(outputFile)).lines().reduce((s, t) -> s.concat("\n").concat(t)).orElse("")); + + /*List contentEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).lines().skip(1).findFirst().get().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 hasSmell = smells.stream().map(x-> expectedSmells.contains(x.getSmellName())?"true":"false").collect(Collectors.toList()); + + expectedEntries.addAll(hasSmell); + + assertIterableEquals(expectedEntries,contentEntries);*/ + } + + @Test + public void testConsistentColumns() throws IOException { + sut = new ReportController(resultsWriter, Arrays.asList(ReportGranularity.FILE)); + sut.report(Arrays.asList(file)); + assertTrue(outputFile.exists(), "Output file missing!"); + + 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/ReportControllerTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java new file mode 100644 index 0000000..0dc96f7 --- /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; + ResultsWriter resultsWriter; + + @BeforeEach + public void setUp() throws IOException { + files = new ArrayList<>(); + smell = mock(AbstractSmell.class); + tf = mock(TestFile.class); + resultsWriter = mock(ResultsWriter.class); + sut = new ReportController(resultsWriter); + } + + @Test + public void testReportDoesNotThrow() { + assertDoesNotThrow(() -> sut.report(files)); + } + + @Test + public void testReportNotCallExportSmells() throws IOException { + sut.report(files); + verify(resultsWriter, never()).exportSmells(any()); + } + + @Test + public void testReportCallExportSmellsFromFile() throws IOException { + files.add(tf); + when(tf.getTestSmells()).thenReturn(Arrays.asList(smell)); + + sut.report(files); + + verify(resultsWriter, 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(resultsWriter, atLeastOnce()).exportSmells(tf); + //verify(resultsWriter, atLeastOnce()).exportSmells(smellyElem); TODO: make exportSmells(smellyElem) be detectable + } +} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIntegrationTest.java index 02be5f4..4598af8 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIntegrationTest.java @@ -3,6 +3,7 @@ import edu.rit.se.testsmells.testsmell.smell.*; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -25,6 +26,7 @@ void tearDown() { testSmellDetector = null; } + @Disabled @Test public void testSmellsFreeProject() throws IOException { TestFile file = new TestFile( From 4f1e4124a7c632105026ff8f16f10b1732d6d195 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Fri, 25 Sep 2020 21:30:03 -0300 Subject: [PATCH 19/42] Implemented merge of smells with same name on report --- .../testsmell/ReportController.java | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java index 76a1b9d..4db6f61 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java @@ -1,9 +1,7 @@ package edu.rit.se.testsmells.testsmell; import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.Properties; +import java.util.*; import java.util.stream.Collectors; enum ReportGranularity {FILE, CLASS, METHOD} @@ -69,13 +67,30 @@ private void reportTestFiles(List files) throws IOException { } } - private void reportSmellyElements(List smells, Class type) throws IOException { - for (AbstractSmell smell : smells) { - List smellyElements = smell.getSmellyElements(); - smellyElements = smellyElements.stream().filter(type::isInstance).collect(Collectors.toList()); - for (SmellyElement elem : smellyElements) { - resultsWriter.exportSmells(elem); + private List mergeSmellyElements(List elements) { + List result = new ArrayList<>(); + for (SmellyElement s1 : elements) { + boolean have = false; + for (SmellyElement s2 : result) { + if (s1 == s2) { + have = true; + } else if (s1.getElementName().equals(s2.getElementName())) { + have = true; + s2.setHasSmell(s2.hasSmell() || s1.hasSmell()); + s1.getData().forEach(s2::addDataItem); + } + } + if (!have) { + result.add(s1); } } + return result; + } + + private void reportSmellyElements(List smells, Class type) throws IOException { + List c = smells.stream().map(AbstractSmell::getSmellyElements).flatMap(Collection::stream).filter(type::isInstance).collect(Collectors.toList()); + for (SmellyElement elem : mergeSmellyElements(c)) { + resultsWriter.exportSmells(elem); + } } } From 8df9dc79eb5b45c0d19f6482128fcfd7a35e9f0c Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Sat, 26 Sep 2020 00:51:12 -0300 Subject: [PATCH 20/42] Reimplemented merging, fixing repeated entry and wrong smellPresence --- .../testsmell/ReportController.java | 83 ++++++++++++++----- .../testsmells/testsmell/ResultsWriter.java | 8 ++ .../testsmell/ReportControllerTest.java | 2 +- 3 files changed, 71 insertions(+), 22 deletions(-) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java index 4db6f61..f9941fc 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java @@ -67,30 +67,71 @@ private void reportTestFiles(List files) throws IOException { } } - private List mergeSmellyElements(List elements) { - List result = new ArrayList<>(); - for (SmellyElement s1 : elements) { - boolean have = false; - for (SmellyElement s2 : result) { - if (s1 == s2) { - have = true; - } else if (s1.getElementName().equals(s2.getElementName())) { - have = true; - s2.setHasSmell(s2.hasSmell() || s1.hasSmell()); - s1.getData().forEach(s2::addDataItem); - } - } - if (!have) { - result.add(s1); - } - } - return result; + private List mergeSmellyElements(List elements) { + List gruopedByElemName = elements.stream().map(c -> c.name).distinct().collect(Collectors.toList()); + return gruopedByElemName.stream().map(name -> ReportOutput.fromCell(filterByName(elements, name))).collect(Collectors.toList()); + } + + private List filterByName(List elements, String name) { + return elements.stream().filter(c1 -> c1.name.equals(name)).collect(Collectors.toList()); } private void reportSmellyElements(List smells, Class type) throws IOException { - List c = smells.stream().map(AbstractSmell::getSmellyElements).flatMap(Collection::stream).filter(type::isInstance).collect(Collectors.toList()); - for (SmellyElement elem : mergeSmellyElements(c)) { - resultsWriter.exportSmells(elem); + List c = smells.stream().flatMap(s -> s.getSmellyElements().stream().filter(type::isInstance).map(se -> ReportCell.fromSmellElem(se, s.getSmellName()))).collect(Collectors.toList()); + for (ReportOutput output : mergeSmellyElements(c)) { + resultsWriter.exportSmells(output); + } + } + + private static class ReportCell { + private String smellType; + private String name; + private Map data; + private boolean hasSmell; + + static ReportCell fromSmellElem(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; + } + } + + static class ReportOutput { + private Map smellsPresence; + private Map data; + private String name; + + public static ReportOutput fromCell(List cells) { + ReportOutput output = new ReportOutput(); + + String elementName = cells.get(0).name; + assert cells.stream().allMatch(s -> s.name.equals(elementName)); + + output.name = elementName; + output.data = new HashMap<>(); + output.smellsPresence = new HashMap<>(); + + cells.forEach(cell -> { + output.data.putAll(cell.data); + boolean hasSmell = output.smellsPresence.getOrDefault(cell.smellType, false) || cell.hasSmell; + output.smellsPresence.put(cell.smellType, hasSmell); + }); + return output; + } + + public Map getSmellsPresence() { + return smellsPresence; + } + + public Map getData() { + return data; + } + + public String getName() { + return name; } } } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java index cfd1fb8..5b67d93 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java @@ -46,6 +46,14 @@ public void writeCSVHeader(TestSmellDetector testSmellDetector, SmellsContainer writeCSV(headers); } + public void exportSmells(ReportController.ReportOutput data) throws IOException { + List entries = new ArrayList<>(); + entries.add(data.getName()); + entries.addAll(data.getData().values()); + data.getSmellsPresence().values().forEach(e -> entries.add(e.toString())); + writeCSV(entries); + } + public void exportSmells(SmellsContainer fileTestSmells) throws IOException { List entries = new ArrayList<>(fileTestSmells.getTestDescriptionEntries().values()); for (AbstractSmell smell : fileTestSmells.getTestSmells()) { diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java index 0dc96f7..07c915e 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java @@ -35,7 +35,7 @@ public void testReportDoesNotThrow() { @Test public void testReportNotCallExportSmells() throws IOException { sut.report(files); - verify(resultsWriter, never()).exportSmells(any()); + verify(resultsWriter, never()).exportSmells((SmellsContainer) any()); } @Test From f0d1c3417b62a5a1120f1c50f0a4308f794e5c28 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Sat, 26 Sep 2020 03:43:56 -0300 Subject: [PATCH 21/42] Extracted Report formatting from ReportController and refactored code --- src/main/java/edu/rit/se/testsmells/Main.java | 2 +- .../testsmells/testsmell/AbstractSmell.java | 2 +- .../testsmell/ExtractingStrategy.java | 10 ++ .../testsmells/testsmell/MethodValidator.java | 12 ++ .../rit/se/testsmells/testsmell/Report.java | 7 + .../testsmell/ReportController.java | 98 ++++---------- .../testsmells/testsmell/ResultsWriter.java | 7 +- .../testsmell/internal/ExtractingByMerge.java | 123 ++++++++++++++++++ .../testsmell/smell/MysteryGuest.java | 52 +------- .../testsmell/MethodValidatorTest.java | 2 +- .../ReportControllerIntegrationTest.java | 10 +- .../testsmell/ReportControllerTest.java | 2 +- 12 files changed, 188 insertions(+), 139 deletions(-) create mode 100644 src/main/java/edu/rit/se/testsmells/testsmell/ExtractingStrategy.java create mode 100644 src/main/java/edu/rit/se/testsmells/testsmell/Report.java create mode 100644 src/main/java/edu/rit/se/testsmells/testsmell/internal/ExtractingByMerge.java diff --git a/src/main/java/edu/rit/se/testsmells/Main.java b/src/main/java/edu/rit/se/testsmells/Main.java index 3901e03..b880a22 100644 --- a/src/main/java/edu/rit/se/testsmells/Main.java +++ b/src/main/java/edu/rit/se/testsmells/Main.java @@ -23,7 +23,7 @@ public static void main(String[] args) throws IOException { List files = readInputTestFiles(inputFile); ResultsWriter resultsWriter = initializeOutputFile(testSmellDetector, files.get(0)); - ReportController reportCtrl = new ReportController(resultsWriter); + ReportController reportCtrl = ReportController.createReportController(resultsWriter); for (TestFile file : files) { System.out.println(getCurrentDateFormatted() + " Processing: " + file.getTestFilePath()); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java index d44e6ea..fa66573 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java @@ -14,7 +14,7 @@ public abstract class AbstractSmell { public abstract String getSmellName(); public AbstractSmell() { - methodValidator = new MethodValidator(); //TODO: dependency should be injected (or, at least, be a singleton) + methodValidator = MethodValidator.getInstance(); smellyElementList = new CopyOnWriteArrayList<>(); } 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 index 02709bb..cfc3ead 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/MethodValidator.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/MethodValidator.java @@ -4,6 +4,18 @@ 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")); } 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..b01ef0f --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/Report.java @@ -0,0 +1,7 @@ +package edu.rit.se.testsmells.testsmell; + +import java.util.List; + +public interface Report { + List getEntryValues(); +} diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java index f9941fc..303262e 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java @@ -1,38 +1,40 @@ package edu.rit.se.testsmells.testsmell; +import edu.rit.se.testsmells.testsmell.internal.ExtractingByMerge; + import java.io.IOException; -import java.util.*; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Properties; import java.util.stream.Collectors; -enum ReportGranularity {FILE, CLASS, METHOD} - public class ReportController { private final ResultsWriter resultsWriter; - private final List configuredGranularties; - private final String PROPERTIES_FILENAME = "test-smells.properties"; - private String PROPERTIES_KEY = "report.granularity"; + private final List configuredGranularities; + private ExtractingStrategy extractor = new ExtractingByMerge(); - public ReportController(ResultsWriter resultsWriter) throws IOException { + ReportController(ResultsWriter resultsWriter, List granularities) { this.resultsWriter = resultsWriter; - configuredGranularties = readProperties(); + configuredGranularities = granularities; } - public ReportController(ResultsWriter resultsWriter, List granularities) { - this.resultsWriter = resultsWriter; - - configuredGranularties = granularities; + public static ReportController createReportController(ResultsWriter resultsWriter) throws IOException { + return new ReportController(resultsWriter, readProperties()); } - private List readProperties() throws IOException { + 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(getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILENAME)); + 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 : configuredGranularties) { + for (ReportGranularity config : configuredGranularities) { switch (config) { case CLASS: reportTestClasses(files); @@ -67,71 +69,17 @@ private void reportTestFiles(List files) throws IOException { } } - private List mergeSmellyElements(List elements) { - List gruopedByElemName = elements.stream().map(c -> c.name).distinct().collect(Collectors.toList()); - return gruopedByElemName.stream().map(name -> ReportOutput.fromCell(filterByName(elements, name))).collect(Collectors.toList()); - } + private List mergeSmellyElements(List smells, Class type) { - private List filterByName(List elements, String name) { - return elements.stream().filter(c1 -> c1.name.equals(name)).collect(Collectors.toList()); + return extractor.extract(smells, type); } private void reportSmellyElements(List smells, Class type) throws IOException { - List c = smells.stream().flatMap(s -> s.getSmellyElements().stream().filter(type::isInstance).map(se -> ReportCell.fromSmellElem(se, s.getSmellName()))).collect(Collectors.toList()); - for (ReportOutput output : mergeSmellyElements(c)) { - resultsWriter.exportSmells(output); + List smellyElementReports = mergeSmellyElements(smells, type); + for (Report report : smellyElementReports) { + resultsWriter.exportSmells(report); } } - private static class ReportCell { - private String smellType; - private String name; - private Map data; - private boolean hasSmell; - - static ReportCell fromSmellElem(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; - } - } - - static class ReportOutput { - private Map smellsPresence; - private Map data; - private String name; - - public static ReportOutput fromCell(List cells) { - ReportOutput output = new ReportOutput(); - - String elementName = cells.get(0).name; - assert cells.stream().allMatch(s -> s.name.equals(elementName)); - - output.name = elementName; - output.data = new HashMap<>(); - output.smellsPresence = new HashMap<>(); - - cells.forEach(cell -> { - output.data.putAll(cell.data); - boolean hasSmell = output.smellsPresence.getOrDefault(cell.smellType, false) || cell.hasSmell; - output.smellsPresence.put(cell.smellType, hasSmell); - }); - return output; - } - - public Map getSmellsPresence() { - return smellsPresence; - } - - public Map getData() { - return data; - } - - public String getName() { - return name; - } - } + enum ReportGranularity {FILE, CLASS, METHOD} } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java index 5b67d93..cfacc70 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java @@ -46,11 +46,8 @@ public void writeCSVHeader(TestSmellDetector testSmellDetector, SmellsContainer writeCSV(headers); } - public void exportSmells(ReportController.ReportOutput data) throws IOException { - List entries = new ArrayList<>(); - entries.add(data.getName()); - entries.addAll(data.getData().values()); - data.getSmellsPresence().values().forEach(e -> entries.add(e.toString())); + public void exportSmells(Report data) throws IOException { + List entries = data.getEntryValues(); writeCSV(entries); } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/internal/ExtractingByMerge.java b/src/main/java/edu/rit/se/testsmells/testsmell/internal/ExtractingByMerge.java new file mode 100644 index 0000000..26cf74c --- /dev/null +++ b/src/main/java/edu/rit/se/testsmells/testsmell/internal/ExtractingByMerge.java @@ -0,0 +1,123 @@ +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 ExtractingByMerge 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 Map smellsPresence; + private Map data; + private String name; + + /** + * 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; + } + + Map getSmellsPresence() { + return smellsPresence; + } + + public Map getData() { + return data; + } + + public String getName() { + return name; + } + + @Override + public List getEntryValues() { + List entries = new ArrayList<>(); + entries.add(getName()); + entries.addAll(getData().values()); + getSmellsPresence().values().forEach(e -> entries.add(e.toString())); + return entries; + } + } +} diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java index 91c0484..21027f8 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java @@ -22,7 +22,7 @@ */ public class MysteryGuest extends AbstractSmell { - + private static final boolean doClassLevelVariableCheck = false; public MysteryGuest() { super(); @@ -62,31 +62,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; @@ -111,33 +86,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/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java index 3ae3a72..f12e73e 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/MethodValidatorTest.java @@ -15,7 +15,7 @@ class MethodValidatorTest { @BeforeEach void setUp() { - sut = new MethodValidator(); + sut = MethodValidator.getInstance(); } @Test diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java index 21363a5..563d64a 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java @@ -72,13 +72,13 @@ public void tearDown() { @Test public void testReport() throws IOException { - sut = new ReportController(resultsWriter, Arrays.asList(ReportGranularity.FILE)); + sut = new ReportController(resultsWriter, Arrays.asList(ReportController.ReportGranularity.FILE)); assertDoesNotThrow(() -> sut.report(Arrays.asList(file))); } @Test public void testHeader() throws IOException { - sut = new ReportController(resultsWriter, Arrays.asList(ReportGranularity.FILE)); + sut = new ReportController(resultsWriter, Arrays.asList(ReportController.ReportGranularity.FILE)); sut.report(Arrays.asList(file)); assertTrue(outputFile.exists(), "Output file missing!"); @@ -93,7 +93,7 @@ public void testHeader() throws IOException { @Test public void testContent_FILE() throws IOException { - sut = new ReportController(resultsWriter, Arrays.asList(ReportGranularity.FILE)); + sut = new ReportController(resultsWriter, Arrays.asList(ReportController.ReportGranularity.FILE)); sut.report(Arrays.asList(file)); assertTrue(outputFile.exists(), "Output file missing!"); @@ -110,7 +110,7 @@ public void testContent_FILE() throws IOException { @Test public void testContent_METHOD() throws IOException { - sut = new ReportController(resultsWriter, Arrays.asList(ReportGranularity.METHOD)); + sut = new ReportController(resultsWriter, Arrays.asList(ReportController.ReportGranularity.METHOD)); sut.report(Arrays.asList(file)); assertTrue(outputFile.exists(), "Output file missing!"); @@ -129,7 +129,7 @@ public void testContent_METHOD() throws IOException { @Test public void testConsistentColumns() throws IOException { - sut = new ReportController(resultsWriter, Arrays.asList(ReportGranularity.FILE)); + sut = new ReportController(resultsWriter, Arrays.asList(ReportController.ReportGranularity.FILE)); sut.report(Arrays.asList(file)); assertTrue(outputFile.exists(), "Output file missing!"); diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java index 07c915e..7e99fab 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java @@ -24,7 +24,7 @@ public void setUp() throws IOException { smell = mock(AbstractSmell.class); tf = mock(TestFile.class); resultsWriter = mock(ResultsWriter.class); - sut = new ReportController(resultsWriter); + sut = ReportController.createReportController(resultsWriter); } @Test From 833911e7625f6c56d8fee9f49daf02a9ccd3b3d5 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Sat, 26 Sep 2020 05:46:16 -0300 Subject: [PATCH 22/42] Adapted tests to ignore outputted Smells order (this is still WIP) --- .../ReportControllerIntegrationTest.java | 66 +++++++++---------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java index 563d64a..328a0e2 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java @@ -22,30 +22,7 @@ public class ReportControllerIntegrationTest { TestFile file; ReportController sut; ResultsWriter resultsWriter; - List expectedSmells = Arrays.asList("Lazy Test", "Eager Test", "Exception Catching Throwing"); - 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() - ); + 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()); String appName = "LazyTest"; String testFilePath = "/LazyTest/src/test/java/com/github/marmaladesky/tests/CryptographerTest.java"; String productionFilePath = "/LazyTest/src/main/java/com/github/marmaladesky/Cryptographer.java"; @@ -54,11 +31,7 @@ public class ReportControllerIntegrationTest { public void setUp() throws IOException { TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); smells.forEach(testSmellDetector::addDetectableSmell); - file = new TestFile( - appName, - testFilePath, - productionFilePath - ); + file = new TestFile(appName, testFilePath, productionFilePath); resultsWriter = ResultsWriter.createResultsWriter(); resultsWriter.writeCSVHeader(testSmellDetector, file); testSmellDetector.detectSmells(file); @@ -101,6 +74,7 @@ public void testContent_FILE() throws IOException { 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); @@ -108,23 +82,43 @@ public void testContent_FILE() throws IOException { assertIterableEquals(expectedEntries, contentEntries); } + 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 - public void testContent_METHOD() throws IOException { + public void testContent_METHOD_Decrypt() throws IOException { sut = new ReportController(resultsWriter, Arrays.asList(ReportController.ReportGranularity.METHOD)); sut.report(Arrays.asList(file)); + assertTrue(outputFile.exists(), "Output file missing!"); - assertEquals("", new BufferedReader(new FileReader(outputFile)).lines().reduce((s, t) -> s.concat("\n").concat(t)).orElse("")); + assertEquals(17, getMethodSmells().size()); - /*List contentEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).lines().skip(1).findFirst().get().split(",")); + List contentEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).lines().skip(1).findFirst().get().split(",")); + assertEquals(2, contentEntries.stream().filter(e -> e.equals("true")).count()); // "Lazy Test" and "Exception Catching Throwing" + assertEquals(15, contentEntries.stream().filter(e -> e.equals("false")).count()); // 17 methods smells - 2 detected smells + } - 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)); + @Test + public void testContent_METHOD_Encrypt() throws IOException { + sut = new ReportController(resultsWriter, Arrays.asList(ReportController.ReportGranularity.METHOD)); + sut.report(Arrays.asList(file)); - List hasSmell = smells.stream().map(x-> expectedSmells.contains(x.getSmellName())?"true":"false").collect(Collectors.toList()); + assertTrue(outputFile.exists(), "Output file missing!"); - expectedEntries.addAll(hasSmell); + assertEquals(17, getMethodSmells().size()); - assertIterableEquals(expectedEntries,contentEntries);*/ + List contentEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).lines().skip(2).findFirst().get().split(",")); + assertEquals(3, contentEntries.stream().filter(e -> e.equals("true")).count()); // "Eager Test", "Lazy Test" and "Exception Catching Throwing" + assertEquals(14, contentEntries.stream().filter(e -> e.equals("false")).count()); // 17 methods smells - 3 detected smells } @Test From f5b317893ce58f627ba0b2b423b278fa3752eb50 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Sat, 26 Sep 2020 05:50:08 -0300 Subject: [PATCH 23/42] Fixed some warnings --- .../ReportControllerIntegrationTest.java | 66 ++++++++++--------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java index 328a0e2..ef24f5f 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java @@ -11,24 +11,25 @@ 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 -public class ReportControllerIntegrationTest { - File outputFile; - TestFile file; - ReportController sut; - ResultsWriter resultsWriter; - 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()); - String appName = "LazyTest"; - String testFilePath = "/LazyTest/src/test/java/com/github/marmaladesky/tests/CryptographerTest.java"; - String productionFilePath = "/LazyTest/src/main/java/com/github/marmaladesky/Cryptographer.java"; +class ReportControllerIntegrationTest { + private File outputFile; + private TestFile file; + private ReportController sut; + private ResultsWriter resultsWriter; + 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 - public void setUp() throws IOException { + void setUp() throws IOException { TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); smells.forEach(testSmellDetector::addDetectableSmell); file = new TestFile(appName, testFilePath, productionFilePath); @@ -39,20 +40,21 @@ public void setUp() throws IOException { } @AfterEach - public void tearDown() { + void tearDown() { + //noinspection ResultOfMethodCallIgnored outputFile.delete(); } @Test - public void testReport() throws IOException { - sut = new ReportController(resultsWriter, Arrays.asList(ReportController.ReportGranularity.FILE)); - assertDoesNotThrow(() -> sut.report(Arrays.asList(file))); + void testReport() { + sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.FILE)); + assertDoesNotThrow(() -> sut.report(Collections.singletonList(file))); } @Test - public void testHeader() throws IOException { - sut = new ReportController(resultsWriter, Arrays.asList(ReportController.ReportGranularity.FILE)); - sut.report(Arrays.asList(file)); + void testHeader() throws IOException { + sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.FILE)); + sut.report(Collections.singletonList(file)); assertTrue(outputFile.exists(), "Output file missing!"); List headerEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).readLine().split(",")); @@ -65,12 +67,12 @@ public void testHeader() throws IOException { } @Test - public void testContent_FILE() throws IOException { - sut = new ReportController(resultsWriter, Arrays.asList(ReportController.ReportGranularity.FILE)); - sut.report(Arrays.asList(file)); + void testContent_FILE() throws IOException { + sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.FILE)); + sut.report(Collections.singletonList(file)); assertTrue(outputFile.exists(), "Output file missing!"); - List contentEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).lines().skip(1).findFirst().get().split(",")); + 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)); @@ -94,37 +96,37 @@ List getMethodSmells() { } @Test - public void testContent_METHOD_Decrypt() throws IOException { - sut = new ReportController(resultsWriter, Arrays.asList(ReportController.ReportGranularity.METHOD)); - sut.report(Arrays.asList(file)); + void testContent_METHOD_Decrypt() throws IOException { + sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); + sut.report(Collections.singletonList(file)); assertTrue(outputFile.exists(), "Output file missing!"); assertEquals(17, getMethodSmells().size()); - List contentEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).lines().skip(1).findFirst().get().split(",")); + 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(15, contentEntries.stream().filter(e -> e.equals("false")).count()); // 17 methods smells - 2 detected smells } @Test - public void testContent_METHOD_Encrypt() throws IOException { - sut = new ReportController(resultsWriter, Arrays.asList(ReportController.ReportGranularity.METHOD)); - sut.report(Arrays.asList(file)); + void testContent_METHOD_Encrypt() throws IOException { + sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); + sut.report(Collections.singletonList(file)); assertTrue(outputFile.exists(), "Output file missing!"); assertEquals(17, getMethodSmells().size()); - List contentEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).lines().skip(2).findFirst().get().split(",")); + 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(14, contentEntries.stream().filter(e -> e.equals("false")).count()); // 17 methods smells - 3 detected smells } @Test - public void testConsistentColumns() throws IOException { - sut = new ReportController(resultsWriter, Arrays.asList(ReportController.ReportGranularity.FILE)); - sut.report(Arrays.asList(file)); + void testConsistentColumns() throws IOException { + sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.FILE)); + sut.report(Collections.singletonList(file)); assertTrue(outputFile.exists(), "Output file missing!"); BufferedReader content = new BufferedReader(new FileReader(outputFile)); From d36797b9848978c85cb1c01145d16f3d0cd601e6 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Sat, 26 Sep 2020 23:46:33 -0300 Subject: [PATCH 24/42] CSV header generated after smell detection + method compatibility --- src/main/java/edu/rit/se/testsmells/Main.java | 9 ++-- .../rit/se/testsmells/testsmell/Report.java | 4 ++ .../testsmell/ReportController.java | 8 ++-- .../testsmells/testsmell/ResultsWriter.java | 44 +++++++++++------ ...{ExtractingByMerge.java => Extractor.java} | 48 +++++++++++++------ .../ReportControllerIntegrationTest.java | 30 ++++++++++-- 6 files changed, 101 insertions(+), 42 deletions(-) rename src/main/java/edu/rit/se/testsmells/testsmell/internal/{ExtractingByMerge.java => Extractor.java} (77%) diff --git a/src/main/java/edu/rit/se/testsmells/Main.java b/src/main/java/edu/rit/se/testsmells/Main.java index b880a22..576ed5b 100644 --- a/src/main/java/edu/rit/se/testsmells/Main.java +++ b/src/main/java/edu/rit/se/testsmells/Main.java @@ -22,7 +22,7 @@ public static void main(String[] args) throws IOException { TestSmellDetector testSmellDetector = initializeSmells(); List files = readInputTestFiles(inputFile); - ResultsWriter resultsWriter = initializeOutputFile(testSmellDetector, files.get(0)); + ResultsWriter resultsWriter = initializeOutputFile(testSmellDetector); ReportController reportCtrl = ReportController.createReportController(resultsWriter); for (TestFile file : files) { @@ -39,12 +39,11 @@ private static Object getCurrentDateFormatted() { return (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()); } - private static ResultsWriter initializeOutputFile(TestSmellDetector testSmellDetector, TestFile anyFile) throws IOException { - ResultsWriter resultsWriter = ResultsWriter.createResultsWriter(); + private static ResultsWriter initializeOutputFile(TestSmellDetector testSmellDetector) throws IOException { - resultsWriter.writeCSVHeader(testSmellDetector, anyFile); +// resultsWriter.writeCSVHeader(testSmellDetector, anyFile); - return resultsWriter; + return ResultsWriter.createResultsWriter(testSmellDetector); } private static List readInputTestFiles(File inputFile) throws IOException { diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/Report.java b/src/main/java/edu/rit/se/testsmells/testsmell/Report.java index b01ef0f..6f70c57 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/Report.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/Report.java @@ -4,4 +4,8 @@ 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 index 303262e..7f92320 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java @@ -1,6 +1,6 @@ package edu.rit.se.testsmells.testsmell; -import edu.rit.se.testsmells.testsmell.internal.ExtractingByMerge; +import edu.rit.se.testsmells.testsmell.internal.Extractor; import java.io.IOException; import java.util.Arrays; @@ -12,7 +12,7 @@ public class ReportController { private final ResultsWriter resultsWriter; private final List configuredGranularities; - private ExtractingStrategy extractor = new ExtractingByMerge(); + private ExtractingStrategy extractor = new Extractor(); ReportController(ResultsWriter resultsWriter, List granularities) { this.resultsWriter = resultsWriter; @@ -76,9 +76,7 @@ private List mergeSmellyElements(List smells, Class ty private void reportSmellyElements(List smells, Class type) throws IOException { List smellyElementReports = mergeSmellyElements(smells, type); - for (Report report : smellyElementReports) { - resultsWriter.exportSmells(report); - } + resultsWriter.exportSmells(smellyElementReports); } enum ReportGranularity {FILE, CLASS, METHOD} diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java index cfacc70..a3a6cc0 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java @@ -6,6 +6,7 @@ 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 @@ -14,44 +15,59 @@ public class ResultsWriter { private String outputFile; private FileWriter writer; + private TestSmellDetector testSmellDetector; + private List headers; /** * Creates the file into which output it to be written into. Results from each file will be stored in a new file * * @throws IOException Failed to create/open output file */ - private ResultsWriter() throws IOException { + private ResultsWriter(TestSmellDetector detector) throws IOException { + testSmellDetector = detector; String time = String.valueOf(Calendar.getInstance().getTimeInMillis()); outputFile = MessageFormat.format("{0}_{1}_{2}.{3}", "Output", "TestSmellDetection", time, "csv"); writer = new FileWriter(outputFile, false); } - public String getOutputFile() { - return outputFile; - } - /** * Factory method that provides a new instance of the ResultsWriter * * @return new ResultsWriter instance * @throws IOException Failed to create/open output file */ - public static ResultsWriter createResultsWriter() throws IOException { - return new ResultsWriter(); + public static ResultsWriter createResultsWriter(TestSmellDetector testSmellDetector) throws IOException { + return new ResultsWriter(testSmellDetector); + } + + String getOutputFile() { + return outputFile; } - public void writeCSVHeader(TestSmellDetector testSmellDetector, SmellsContainer anyFile) throws IOException { + private void writeCSVHeader(List reports) throws IOException { + headers = reports.stream().flatMap(report -> report.getEntryKeys().stream()).distinct().collect(Collectors.toList()); + writeCSV(headers); + } + + private void writeCSVHeader(SmellsContainer anyFile) throws IOException { List headers = new ArrayList<>(anyFile.getTestDescriptionEntries().keySet()); - headers.addAll(testSmellDetector.getTestSmellNames()); + headers.addAll(testSmellDetector.getTestSmellNames()); //TODO: remove testSmellDetector dependency writeCSV(headers); } - public void exportSmells(Report data) throws IOException { - List entries = data.getEntryValues(); - writeCSV(entries); + void exportSmells(List reports) throws IOException { + writeCSVHeader(reports); + for (Report report : reports) { + List entries = new ArrayList<>(); + for (String column : headers) { + entries.add(report.getValue(column)); + } + writeCSV(entries); + } } - public void exportSmells(SmellsContainer fileTestSmells) throws IOException { + void exportSmells(SmellsContainer fileTestSmells) throws IOException { + writeCSVHeader(fileTestSmells); List entries = new ArrayList<>(fileTestSmells.getTestDescriptionEntries().values()); for (AbstractSmell smell : fileTestSmells.getTestSmells()) { try { @@ -69,7 +85,7 @@ public void exportSmells(SmellsContainer fileTestSmells) throws IOException { * @param dataValues the data that needs to be written into the file * @throws IOException Failed to create/open output file */ - public void writeCSV(List dataValues) throws IOException { + private void writeCSV(List dataValues) throws IOException { writer = new FileWriter(outputFile, true); for (int i = 0; i < dataValues.size(); i++) { diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/internal/ExtractingByMerge.java b/src/main/java/edu/rit/se/testsmells/testsmell/internal/Extractor.java similarity index 77% rename from src/main/java/edu/rit/se/testsmells/testsmell/internal/ExtractingByMerge.java rename to src/main/java/edu/rit/se/testsmells/testsmell/internal/Extractor.java index 26cf74c..d98d902 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/internal/ExtractingByMerge.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/internal/Extractor.java @@ -12,7 +12,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public class ExtractingByMerge implements ExtractingStrategy { +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())); @@ -68,9 +69,12 @@ static ReportCell fromSmellyElement(SmellyElement elem, String smellType) { * 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 @@ -99,25 +103,39 @@ static ReportOutput fromCell(List cells) { return output; } - Map getSmellsPresence() { - return smellsPresence; - } - - public Map getData() { - return data; + @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; } - public String getName() { - return name; + @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 List getEntryValues() { - List entries = new ArrayList<>(); - entries.add(getName()); - entries.addAll(getData().values()); - getSmellsPresence().values().forEach(e -> entries.add(e.toString())); - return entries; + 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/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java index ef24f5f..d8600a5 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java @@ -33,8 +33,7 @@ void setUp() throws IOException { TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); smells.forEach(testSmellDetector::addDetectableSmell); file = new TestFile(appName, testFilePath, productionFilePath); - resultsWriter = ResultsWriter.createResultsWriter(); - resultsWriter.writeCSVHeader(testSmellDetector, file); + resultsWriter = ResultsWriter.createResultsWriter(testSmellDetector); testSmellDetector.detectSmells(file); outputFile = new File(resultsWriter.getOutputFile()); } @@ -52,7 +51,7 @@ void testReport() { } @Test - void testHeader() throws IOException { + void testHeader_FILE() throws IOException { sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.FILE)); sut.report(Collections.singletonList(file)); assertTrue(outputFile.exists(), "Output file missing!"); @@ -95,6 +94,31 @@ List getMethodSmells() { return methodSmells; } + @Test + void testReport_METHOD() throws IOException { + sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); + sut.report(Collections.singletonList(file)); + assertTrue(outputFile.exists(), "Output file missing!"); + + List entries = new BufferedReader(new FileReader(outputFile)).lines().collect(Collectors.toList()); + + + assertEquals("Element Name,WhileCount,ConditionCount,RedundantCount,AssertCount,IfCount,ExceptionCount,ForeachCount,PrintCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,SensitiveCount,MagicNumberCount,Assertion Roulette,Eager Test,Mystery Guest,Sleepy Test,Unknown Test,Redundant Assertion,Magic Number Test,Conditional Test Logic,EmptyTest,General Fixture,Sensitive Equality,Verbose Test,Resource Optimism,Duplicate Assert,Exception Catching Throwing,Print Statement,Lazy Test", entries.get(0)); + assertEquals("testDecrypt,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,true", entries.get(1)); + assertEquals("testEncrypt,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,false,true,false,false,false,false,false,false,false,false,false,false,false,false,true,false,true", entries.get(2)); + } + + @Test + void testHeader_METHOD() throws IOException { + sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); + sut.report(Collections.singletonList(file)); + assertTrue(outputFile.exists(), "Output file missing!"); + + List headerEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).readLine().split(",")); + + assertEquals("Element Name,WhileCount,ConditionCount,RedundantCount,AssertCount,IfCount,ExceptionCount,ForeachCount,PrintCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,SensitiveCount,MagicNumberCount,Assertion Roulette,Eager Test,Mystery Guest,Sleepy Test,Unknown Test,Redundant Assertion,Magic Number Test,Conditional Test Logic,EmptyTest,General Fixture,Sensitive Equality,Verbose Test,Resource Optimism,Duplicate Assert,Exception Catching Throwing,Print Statement,Lazy Test", String.join(",", headerEntries)/*, String.join(",",expectedEntries)*/); + } + @Test void testContent_METHOD_Decrypt() throws IOException { sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); From ea58040cb0cfe120c1bbe2ce501d4104b22167c8 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Sun, 27 Sep 2020 07:06:55 -0300 Subject: [PATCH 25/42] Removed ResultsWriter dependency TestSmellDetector --- src/main/java/edu/rit/se/testsmells/Main.java | 2 +- .../edu/rit/se/testsmells/testsmell/ResultsWriter.java | 10 ++++------ .../testsmell/ReportControllerIntegrationTest.java | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/edu/rit/se/testsmells/Main.java b/src/main/java/edu/rit/se/testsmells/Main.java index 576ed5b..4fa0746 100644 --- a/src/main/java/edu/rit/se/testsmells/Main.java +++ b/src/main/java/edu/rit/se/testsmells/Main.java @@ -43,7 +43,7 @@ private static ResultsWriter initializeOutputFile(TestSmellDetector testSmellDet // resultsWriter.writeCSVHeader(testSmellDetector, anyFile); - return ResultsWriter.createResultsWriter(testSmellDetector); + return ResultsWriter.createResultsWriter(); } private static List readInputTestFiles(File inputFile) throws IOException { diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java index a3a6cc0..579c921 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java @@ -15,7 +15,6 @@ public class ResultsWriter { private String outputFile; private FileWriter writer; - private TestSmellDetector testSmellDetector; private List headers; /** @@ -23,8 +22,7 @@ public class ResultsWriter { * * @throws IOException Failed to create/open output file */ - private ResultsWriter(TestSmellDetector detector) throws IOException { - testSmellDetector = detector; + 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); @@ -36,8 +34,8 @@ private ResultsWriter(TestSmellDetector detector) throws IOException { * @return new ResultsWriter instance * @throws IOException Failed to create/open output file */ - public static ResultsWriter createResultsWriter(TestSmellDetector testSmellDetector) throws IOException { - return new ResultsWriter(testSmellDetector); + public static ResultsWriter createResultsWriter() throws IOException { + return new ResultsWriter(); } String getOutputFile() { @@ -51,7 +49,7 @@ private void writeCSVHeader(List reports) throws IOException { private void writeCSVHeader(SmellsContainer anyFile) throws IOException { List headers = new ArrayList<>(anyFile.getTestDescriptionEntries().keySet()); - headers.addAll(testSmellDetector.getTestSmellNames()); //TODO: remove testSmellDetector dependency + headers.addAll(anyFile.getTestSmells().stream().map(AbstractSmell::getSmellName).collect(Collectors.toList())); writeCSV(headers); } diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java index d8600a5..1bba1c4 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java @@ -33,7 +33,7 @@ void setUp() throws IOException { TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); smells.forEach(testSmellDetector::addDetectableSmell); file = new TestFile(appName, testFilePath, productionFilePath); - resultsWriter = ResultsWriter.createResultsWriter(testSmellDetector); + resultsWriter = ResultsWriter.createResultsWriter(); testSmellDetector.detectSmells(file); outputFile = new File(resultsWriter.getOutputFile()); } From 2a357fdf09fb8ed2a96567fd5f0dc39689364957 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Sun, 27 Sep 2020 07:48:29 -0300 Subject: [PATCH 26/42] TestMethod full name (package.class.method) retrieved for uniqueness --- .../se/testsmells/testsmell/AbstractSmell.java | 16 ++++++++++++++++ .../testsmell/smell/AssertionRoulette.java | 6 ++++-- .../testsmell/smell/ConditionalTestLogic.java | 7 +++++-- .../testsmell/smell/DependentTest.java | 2 +- .../testsmell/smell/DuplicateAssert.java | 6 ++++-- .../se/testsmells/testsmell/smell/EagerTest.java | 6 ++++-- .../se/testsmells/testsmell/smell/EmptyTest.java | 6 ++++-- .../smell/ExceptionCatchingThrowing.java | 6 ++++-- .../testsmell/smell/GeneralFixture.java | 6 ++++-- .../testsmells/testsmell/smell/IgnoredTest.java | 8 +++++--- .../se/testsmells/testsmell/smell/LazyTest.java | 10 ++++++---- .../testsmell/smell/MagicNumberTest.java | 6 ++++-- .../testsmells/testsmell/smell/MysteryGuest.java | 6 ++++-- .../testsmell/smell/PrintStatement.java | 6 ++++-- .../testsmell/smell/RedundantAssertion.java | 6 ++++-- .../testsmell/smell/ResourceOptimism.java | 6 ++++-- .../testsmell/smell/SensitiveEquality.java | 6 ++++-- .../testsmells/testsmell/smell/SleepyTest.java | 6 ++++-- .../testsmells/testsmell/smell/UnknownTest.java | 6 ++++-- .../testsmells/testsmell/smell/VerboseTest.java | 6 ++++-- .../ReportControllerIntegrationTest.java | 4 ++-- 21 files changed, 95 insertions(+), 42 deletions(-) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java index fa66573..aab3944 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java @@ -1,7 +1,11 @@ package edu.rit.se.testsmells.testsmell; import com.github.javaparser.ast.CompilationUnit; +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.nodeTypes.NodeWithSimpleName; import java.io.FileNotFoundException; import java.util.List; @@ -18,6 +22,18 @@ public AbstractSmell() { smellyElementList = new CopyOnWriteArrayList<>(); } + protected String getFullMethodName(CompilationUnit unit, MethodDeclaration method) { + String packageClassPrefixWithDot = getPackageClass(unit); + + String className = method.getParentNode().map(x -> (ClassOrInterfaceDeclaration) x).map(NodeWithSimpleName::getNameAsString).orElse(""); + + return packageClassPrefixWithDot + className + "." + method.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() { diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java index 6b231db..a49d5c2 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java @@ -17,6 +17,7 @@ public class AssertionRoulette extends AbstractSmell { private int assertionsCount = 0; + private CompilationUnit testFileCompilationUnit; public AssertionRoulette() { super(); @@ -37,7 +38,8 @@ public String getSmellName() { 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,7 +60,7 @@ private class ClassVisitor extends VoidVisitorAdapter { public void visit(MethodDeclaration n, Void arg) { 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); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java index 5d24b90..069dfa5 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java @@ -15,6 +15,8 @@ */ public class ConditionalTestLogic extends AbstractSmell { + private CompilationUnit testFileCompilationUnit; + public ConditionalTestLogic() { super(); } @@ -35,7 +37,8 @@ public String getSmellName() { 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); } private class ClassVisitor extends VoidVisitorAdapter { @@ -48,7 +51,7 @@ private class ClassVisitor extends VoidVisitorAdapter { public void visit(MethodDeclaration n, Void arg) { 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); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java index 60602ba..1c7fc87 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java @@ -40,7 +40,7 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit for (TestMethod testMethod : testMethods) { if (testMethod.getCalledMethods().stream().anyMatch(x -> x.getName().equals(testMethods.stream().map(z -> z.getMethodDeclaration().getNameAsString())))){ - addSmellyElement(new edu.rit.se.testsmells.testsmell.TestMethod(testMethod.getMethodDeclaration().getNameAsString())); + addSmellyElement(new edu.rit.se.testsmells.testsmell.TestMethod(getFullMethodName(testFileCompilationUnit, testMethod.getMethodDeclaration()))); } } } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java index ef9cb97..2ae67a7 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java @@ -16,6 +16,7 @@ public class DuplicateAssert extends AbstractSmell { + private CompilationUnit testFileCompilationUnit; public DuplicateAssert() { super(); @@ -36,7 +37,8 @@ public String getSmellName() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { DuplicateAssert.ClassVisitor classVisitor; classVisitor = new DuplicateAssert.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } @@ -51,7 +53,7 @@ private class ClassVisitor extends VoidVisitorAdapter { public void visit(MethodDeclaration n, Void arg) { 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); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java index d7cf68d..4cf5709 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java @@ -25,6 +25,7 @@ public class EagerTest extends AbstractSmell { private List productionMethods; private int eagerCount; + private CompilationUnit testFileCompilationUnit; public EagerTest() { super(); @@ -54,7 +55,8 @@ 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; } @@ -103,7 +105,7 @@ public void visit(MethodDeclaration n, Void arg) { if (Objects.equals(fileType, TEST_FILE)) { 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); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java index 89d778c..04183b3 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java @@ -15,6 +15,7 @@ public class EmptyTest extends AbstractSmell { + private CompilationUnit testFileCompilationUnit; public EmptyTest() { super(); @@ -35,7 +36,8 @@ public String getSmellName() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { EmptyTest.ClassVisitor classVisitor; classVisitor = new EmptyTest.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } /** @@ -50,7 +52,7 @@ private class ClassVisitor extends VoidVisitorAdapter { @Override public void visit(MethodDeclaration n, Void arg) { if (isValidTestMethod(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 if (!n.isAbstract()) { diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java index 06e18ea..f60c550 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java @@ -17,6 +17,7 @@ public class ExceptionCatchingThrowing extends AbstractSmell { + private CompilationUnit testFileCompilationUnit; public ExceptionCatchingThrowing() { super(); @@ -37,7 +38,8 @@ public String getSmellName() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { ExceptionCatchingThrowing.ClassVisitor classVisitor; classVisitor = new ExceptionCatchingThrowing.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -51,7 +53,7 @@ private class ClassVisitor extends VoidVisitorAdapter { public void visit(MethodDeclaration n, Void arg) { 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); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java index c83977e..e42f033 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java @@ -24,6 +24,7 @@ public class GeneralFixture extends AbstractSmell { MethodDeclaration setupMethod; List fieldList; List setupFields; + private CompilationUnit testFileCompilationUnit; public GeneralFixture() { super(); @@ -44,7 +45,8 @@ public String getSmellName() { 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) { @@ -119,7 +121,7 @@ public void visit(MethodDeclaration n, Void arg) { //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()); addSmellyElement(testMethod); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java index 4beae16..c91e902 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java @@ -14,6 +14,7 @@ public class IgnoredTest extends AbstractSmell { + private CompilationUnit testFileCompilationUnit; public IgnoredTest() { super(); @@ -34,7 +35,8 @@ public String getSmellName() { 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); } /** @@ -67,7 +69,7 @@ public void visit(MethodDeclaration n, Void arg) { //check if test method has Ignore annotation if (n.getAnnotationByName("Test").isPresent()) { if (n.getAnnotationByName("Ignore").isPresent()) { - testMethod = new TestMethod(n.getNameAsString()); + testMethod = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(true); addSmellyElement(testMethod); return; @@ -78,7 +80,7 @@ public void visit(MethodDeclaration n, Void arg) { //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 = new TestMethod(getFullMethodName(testFileCompilationUnit, n)); testMethod.setHasSmell(true); addSmellyElement(testMethod); return; diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java index c64b439..fa65272 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java @@ -25,6 +25,7 @@ public class LazyTest extends AbstractSmell { private List calledProductionMethods; private List productionMethods; + private CompilationUnit testFileCompilationUnit; public LazyTest() { super(); @@ -55,7 +56,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()); @@ -126,7 +128,7 @@ public void visit(MethodDeclaration n, Void arg) { if (Objects.equals(fileType, TEST_FILE)) { 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); @@ -161,7 +163,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) { @@ -170,7 +172,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/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java index 5f06e03..9d475e4 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java @@ -14,6 +14,7 @@ public class MagicNumberTest extends AbstractSmell { + private CompilationUnit testFileCompilationUnit; public MagicNumberTest() { super(); @@ -34,7 +35,8 @@ public String getSmellName() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { MagicNumberTest.ClassVisitor classVisitor; classVisitor = new MagicNumberTest.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -47,7 +49,7 @@ private class ClassVisitor extends VoidVisitorAdapter { public void visit(MethodDeclaration n, Void arg) { 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); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java index 21027f8..4d4b492 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java @@ -23,6 +23,7 @@ public class MysteryGuest extends AbstractSmell { private static final boolean doClassLevelVariableCheck = false; + private CompilationUnit testFileCompilationUnit; public MysteryGuest() { super(); @@ -43,7 +44,8 @@ public String getSmellName() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { MysteryGuest.ClassVisitor classVisitor; classVisitor = new MysteryGuest.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -71,7 +73,7 @@ private class ClassVisitor extends VoidVisitorAdapter { public void visit(MethodDeclaration n, Void arg) { 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); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java index 60fead2..92a4df4 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java @@ -18,6 +18,7 @@ This code checks the body of each test method if System.out. print(), println(), public class PrintStatement extends AbstractSmell { + private CompilationUnit testFileCompilationUnit; public PrintStatement() { super(); @@ -38,7 +39,8 @@ public String getSmellName() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { PrintStatement.ClassVisitor classVisitor; classVisitor = new PrintStatement.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -51,7 +53,7 @@ private class ClassVisitor extends VoidVisitorAdapter { public void visit(MethodDeclaration n, Void arg) { 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); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java index f4a1446..65a9548 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java @@ -17,6 +17,7 @@ public class RedundantAssertion extends AbstractSmell { + private CompilationUnit testFileCompilationUnit; public RedundantAssertion() { super(); @@ -37,7 +38,8 @@ public String getSmellName() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { RedundantAssertion.ClassVisitor classVisitor; classVisitor = new RedundantAssertion.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -50,7 +52,7 @@ private class ClassVisitor extends VoidVisitorAdapter { public void visit(MethodDeclaration n, Void arg) { 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); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java index 721df1f..4809624 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java @@ -19,6 +19,7 @@ public class ResourceOptimism extends AbstractSmell { + private CompilationUnit testFileCompilationUnit; public ResourceOptimism() { super(); @@ -39,7 +40,8 @@ public String getSmellName() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { ResourceOptimism.ClassVisitor classVisitor; classVisitor = new ResourceOptimism.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } @@ -57,7 +59,7 @@ private class ClassVisitor extends VoidVisitorAdapter { public void visit(MethodDeclaration n, Void arg) { 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); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java index 5d93bb8..1634da2 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java @@ -13,6 +13,7 @@ public class SensitiveEquality extends AbstractSmell { + private CompilationUnit testFileCompilationUnit; public SensitiveEquality() { super(); @@ -33,7 +34,8 @@ public String getSmellName() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { SensitiveEquality.ClassVisitor classVisitor; classVisitor = new SensitiveEquality.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -46,7 +48,7 @@ private class ClassVisitor extends VoidVisitorAdapter { public void visit(MethodDeclaration n, Void arg) { 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); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java index bb9651c..fec2a6f 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java @@ -17,6 +17,7 @@ public class SleepyTest extends AbstractSmell { + private CompilationUnit testFileCompilationUnit; public SleepyTest() { super(); @@ -37,7 +38,8 @@ public String getSmellName() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { SleepyTest.ClassVisitor classVisitor; classVisitor = new SleepyTest.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -50,7 +52,7 @@ private class ClassVisitor extends VoidVisitorAdapter { public void visit(MethodDeclaration n, Void arg) { 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); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java index 70b6155..2ece260 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java @@ -18,6 +18,7 @@ public class UnknownTest extends AbstractSmell { + private CompilationUnit testFileCompilationUnit; public UnknownTest() { super(); @@ -38,7 +39,8 @@ public String getSmellName() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { UnknownTest.ClassVisitor classVisitor; classVisitor = new UnknownTest.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } @@ -67,7 +69,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); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java index 045b415..1e4e04a 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java @@ -14,6 +14,7 @@ public class VerboseTest extends AbstractSmell { + private CompilationUnit testFileCompilationUnit; public VerboseTest() { super(); @@ -34,7 +35,8 @@ public String getSmellName() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { VerboseTest.ClassVisitor classVisitor; classVisitor = new VerboseTest.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -48,7 +50,7 @@ private class ClassVisitor extends VoidVisitorAdapter { public void visit(MethodDeclaration n, Void arg) { 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 diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java index 1bba1c4..1c94edc 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java @@ -104,8 +104,8 @@ void testReport_METHOD() throws IOException { assertEquals("Element Name,WhileCount,ConditionCount,RedundantCount,AssertCount,IfCount,ExceptionCount,ForeachCount,PrintCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,SensitiveCount,MagicNumberCount,Assertion Roulette,Eager Test,Mystery Guest,Sleepy Test,Unknown Test,Redundant Assertion,Magic Number Test,Conditional Test Logic,EmptyTest,General Fixture,Sensitive Equality,Verbose Test,Resource Optimism,Duplicate Assert,Exception Catching Throwing,Print Statement,Lazy Test", entries.get(0)); - assertEquals("testDecrypt,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,true", entries.get(1)); - assertEquals("testEncrypt,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,false,true,false,false,false,false,false,false,false,false,false,false,false,false,true,false,true", entries.get(2)); + assertEquals("com.github.marmaladesky.tests.CryptographerTest.testDecrypt,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,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,1,0,1,0,0,0,0,0,0,0,0,0,0,false,true,false,false,false,false,false,false,false,false,false,false,false,false,true,false,true", entries.get(2)); } @Test From 7e8e887c5ed3d2404a02d0c1da3c61f9c3f3ad79 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Sun, 27 Sep 2020 08:05:00 -0300 Subject: [PATCH 27/42] full class name (package.class) retrieved too; made methods more generic --- .../rit/se/testsmells/testsmell/AbstractSmell.java | 12 ++++++++---- .../testsmell/smell/ConstructorInitialization.java | 6 ++++-- .../se/testsmells/testsmell/smell/DefaultTest.java | 7 +++++-- .../se/testsmells/testsmell/smell/IgnoredTest.java | 2 +- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java index aab3944..15e5f04 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java @@ -1,6 +1,7 @@ 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; @@ -22,12 +23,15 @@ public AbstractSmell() { smellyElementList = new CopyOnWriteArrayList<>(); } - protected String getFullMethodName(CompilationUnit unit, MethodDeclaration method) { - String packageClassPrefixWithDot = getPackageClass(unit); + protected String getFullMethodName(CompilationUnit unit, T node) { - String className = method.getParentNode().map(x -> (ClassOrInterfaceDeclaration) x).map(NodeWithSimpleName::getNameAsString).orElse(""); + String className = node.getParentNode().map(x -> (ClassOrInterfaceDeclaration) x).map(NodeWithSimpleName::getNameAsString).orElse(""); - return packageClassPrefixWithDot + className + "." + method.getNameAsString(); + return getPackageClass(unit) + className + "." + node.getNameAsString(); + } + + protected String getFullClassName(CompilationUnit unit, NodeWithSimpleName node) { + return getPackageClass(unit) + node.getNameAsString(); } private String getPackageClass(CompilationUnit unit) { diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java index a74428c..94b4e65 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java @@ -17,6 +17,7 @@ This class checks if the code file contains a Constructor. Ideally, the test sui */ public class ConstructorInitialization extends AbstractSmell { private String testFileName; + private CompilationUnit testFileCompilationUnit; public ConstructorInitialization() { super(); @@ -38,7 +39,8 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit,CompilationUnit this.testFileName = testFileName; ConstructorInitialization.ClassVisitor classVisitor; classVisitor = new ConstructorInitialization.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } @@ -60,7 +62,7 @@ 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 = new TestClass(getFullClassName(testFileCompilationUnit, n)); testClass.setHasSmell(true); addSmellyElement(testClass); } 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 index 34591b1..fe777a2 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DefaultTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DefaultTest.java @@ -15,6 +15,8 @@ public class DefaultTest extends AbstractSmell { + private CompilationUnit testFileCompilationUnit; + public DefaultTest() { super(); } @@ -31,7 +33,8 @@ public String getSmellName() { public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { DefaultTest.ClassVisitor classVisitor; classVisitor = new DefaultTest.ClassVisitor(); - classVisitor.visit(testFileCompilationUnit, null); + this.testFileCompilationUnit = testFileCompilationUnit; + classVisitor.visit(this.testFileCompilationUnit, null); } private class ClassVisitor extends VoidVisitorAdapter { @@ -40,7 +43,7 @@ private class ClassVisitor extends VoidVisitorAdapter { @Override public void visit(ClassOrInterfaceDeclaration n, Void arg) { if (n.getNameAsString().equals("ExampleUnitTest") || n.getNameAsString().equals("ExampleInstrumentedTest")) { - testClass = new TestClass(n.getNameAsString()); + testClass = new TestClass(getFullClassName(testFileCompilationUnit, n)); testClass.setHasSmell(true); addSmellyElement(testClass); } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java index c91e902..9c19133 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java @@ -52,7 +52,7 @@ private class ClassVisitor extends VoidVisitorAdapter { @Override public void visit(ClassOrInterfaceDeclaration n, Void arg) { if (n.getAnnotationByName("Ignore").isPresent()) { - testClass = new TestClass(n.getNameAsString()); + testClass = new TestClass(getFullClassName(testFileCompilationUnit, n)); testClass.setHasSmell(true); addSmellyElement(testClass); } From 57c0335047a952972d5ff68ce37d00d7d74d2b4d Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Sun, 27 Sep 2020 15:42:36 -0300 Subject: [PATCH 28/42] Implemented simultaneous multiple output granularities --- src/main/java/edu/rit/se/testsmells/Main.java | 10 ++-- .../{ResultsWriter.java => CSVWriter.java} | 35 +++++++----- .../testsmell/ReportController.java | 16 +++--- .../ReportControllerIntegrationTest.java | 53 +++++++++++++++---- .../testsmell/ReportControllerTest.java | 12 ++--- 5 files changed, 84 insertions(+), 42 deletions(-) rename src/main/java/edu/rit/se/testsmells/testsmell/{ResultsWriter.java => CSVWriter.java} (80%) diff --git a/src/main/java/edu/rit/se/testsmells/Main.java b/src/main/java/edu/rit/se/testsmells/Main.java index 4fa0746..252229c 100644 --- a/src/main/java/edu/rit/se/testsmells/Main.java +++ b/src/main/java/edu/rit/se/testsmells/Main.java @@ -1,7 +1,7 @@ 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.ResultsWriter; import edu.rit.se.testsmells.testsmell.TestFile; import edu.rit.se.testsmells.testsmell.TestSmellDetector; import edu.rit.se.testsmells.testsmell.smell.*; @@ -22,8 +22,8 @@ public static void main(String[] args) throws IOException { TestSmellDetector testSmellDetector = initializeSmells(); List files = readInputTestFiles(inputFile); - ResultsWriter resultsWriter = initializeOutputFile(testSmellDetector); - ReportController reportCtrl = ReportController.createReportController(resultsWriter); + CSVWriter csvWriter = initializeOutputFile(testSmellDetector); + ReportController reportCtrl = ReportController.createReportController(csvWriter); for (TestFile file : files) { System.out.println(getCurrentDateFormatted() + " Processing: " + file.getTestFilePath()); @@ -39,11 +39,11 @@ private static Object getCurrentDateFormatted() { return (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()); } - private static ResultsWriter initializeOutputFile(TestSmellDetector testSmellDetector) throws IOException { + private static CSVWriter initializeOutputFile(TestSmellDetector testSmellDetector) throws IOException { // resultsWriter.writeCSVHeader(testSmellDetector, anyFile); - return ResultsWriter.createResultsWriter(); + return CSVWriter.createResultsWriter(); } private static List readInputTestFiles(File inputFile) throws IOException { diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java b/src/main/java/edu/rit/se/testsmells/testsmell/CSVWriter.java similarity index 80% rename from src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java rename to src/main/java/edu/rit/se/testsmells/testsmell/CSVWriter.java index 579c921..ef2932e 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ResultsWriter.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/CSVWriter.java @@ -11,35 +11,43 @@ /** * This class is utilized to write output to a CSV file */ -public class ResultsWriter { +public class CSVWriter { - private String outputFile; + 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 * - * @throws IOException Failed to create/open output file */ - private ResultsWriter() throws IOException { + private CSVWriter() { String time = String.valueOf(Calendar.getInstance().getTimeInMillis()); - outputFile = MessageFormat.format("{0}_{1}_{2}.{3}", "Output", "TestSmellDetection", time, "csv"); - writer = new FileWriter(outputFile, false); + 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 - * @throws IOException Failed to create/open output file */ - public static ResultsWriter createResultsWriter() throws IOException { - return new ResultsWriter(); + public static CSVWriter createResultsWriter() { + return new CSVWriter(); } - String getOutputFile() { - return outputFile; + 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; } private void writeCSVHeader(List reports) throws IOException { @@ -84,8 +92,6 @@ void exportSmells(SmellsContainer fileTestSmells) throws IOException { * @throws IOException Failed to create/open output file */ private void writeCSV(List dataValues) throws IOException { - writer = new FileWriter(outputFile, true); - for (int i = 0; i < dataValues.size(); i++) { writer.append(String.valueOf(dataValues.get(i))); @@ -93,6 +99,9 @@ private void writeCSV(List dataValues) throws IOException { else writer.append(System.lineSeparator()); } + } + + void closeFile() throws IOException { writer.flush(); writer.close(); } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java index 7f92320..f76066d 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java @@ -10,18 +10,18 @@ import java.util.stream.Collectors; public class ReportController { - private final ResultsWriter resultsWriter; + private final CSVWriter csvWriter; private final List configuredGranularities; private ExtractingStrategy extractor = new Extractor(); - ReportController(ResultsWriter resultsWriter, List granularities) { - this.resultsWriter = resultsWriter; + ReportController(CSVWriter csvWriter, List granularities) { + this.csvWriter = csvWriter; configuredGranularities = granularities; } - public static ReportController createReportController(ResultsWriter resultsWriter) throws IOException { - return new ReportController(resultsWriter, readProperties()); + public static ReportController createReportController(CSVWriter csvWriter) throws IOException { + return new ReportController(csvWriter, readProperties()); } private static List readProperties() throws IOException { @@ -35,6 +35,7 @@ private static List readProperties() throws IOException { public void report(List files) throws IOException { for (ReportGranularity config : configuredGranularities) { + csvWriter.setOutputFilePrefix(config.toString()); switch (config) { case CLASS: reportTestClasses(files); @@ -48,6 +49,7 @@ public void report(List files) throws IOException { default: throw new IllegalStateException("Unexpected value: " + config); } + csvWriter.closeFile(); } } @@ -65,7 +67,7 @@ private void reportTestClasses(List files) throws IOException { private void reportTestFiles(List files) throws IOException { for (TestFile file : files) { - resultsWriter.exportSmells(file); + csvWriter.exportSmells(file); } } @@ -76,7 +78,7 @@ private List mergeSmellyElements(List smells, Class ty private void reportSmellyElements(List smells, Class type) throws IOException { List smellyElementReports = mergeSmellyElements(smells, type); - resultsWriter.exportSmells(smellyElementReports); + csvWriter.exportSmells(smellyElementReports); } enum ReportGranularity {FILE, CLASS, METHOD} diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java index 1c94edc..4259d82 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java @@ -22,7 +22,7 @@ class ReportControllerIntegrationTest { private File outputFile; private TestFile file; private ReportController sut; - private ResultsWriter resultsWriter; + 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"; @@ -33,9 +33,8 @@ void setUp() throws IOException { TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); smells.forEach(testSmellDetector::addDetectableSmell); file = new TestFile(appName, testFilePath, productionFilePath); - resultsWriter = ResultsWriter.createResultsWriter(); + csvWriter = CSVWriter.createResultsWriter(); testSmellDetector.detectSmells(file); - outputFile = new File(resultsWriter.getOutputFile()); } @AfterEach @@ -44,16 +43,42 @@ void tearDown() { outputFile.delete(); } + @Test + void testMultipleOutput() throws IOException { + List outputs = Arrays.asList(ReportController.ReportGranularity.METHOD, ReportController.ReportGranularity.CLASS, ReportController.ReportGranularity.FILE); + sut = new ReportController(csvWriter, outputs); + sut.report(Collections.singletonList(file)); + + outputFile = new File("METHOD_" + csvWriter.getSuffix()); + 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()); + + outputFile = new File("CLASS_" + csvWriter.getSuffix()); + assertTrue(outputFile.exists(), "Class output file missing!"); + 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()); // TODO: FIX class not being reported + + outputFile = new File("FILE_" + csvWriter.getSuffix()); + assertTrue(outputFile.exists(), "File output file missing!"); + 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 testReport() { - sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.FILE)); + 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 { - sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.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!"); List headerEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).readLine().split(",")); @@ -67,8 +92,9 @@ void testHeader_FILE() throws IOException { @Test void testContent_FILE() throws IOException { - sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.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!"); List contentEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).lines().skip(1).findFirst().orElse("").split(",")); @@ -96,8 +122,9 @@ List getMethodSmells() { @Test void testReport_METHOD() throws IOException { - sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); + sut = new ReportController(csvWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); sut.report(Collections.singletonList(file)); + outputFile = new File(csvWriter.getFilename()); assertTrue(outputFile.exists(), "Output file missing!"); List entries = new BufferedReader(new FileReader(outputFile)).lines().collect(Collectors.toList()); @@ -110,8 +137,9 @@ void testReport_METHOD() throws IOException { @Test void testHeader_METHOD() throws IOException { - sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); + sut = new ReportController(csvWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); sut.report(Collections.singletonList(file)); + outputFile = new File(csvWriter.getFilename()); assertTrue(outputFile.exists(), "Output file missing!"); List headerEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).readLine().split(",")); @@ -121,8 +149,9 @@ void testHeader_METHOD() throws IOException { @Test void testContent_METHOD_Decrypt() throws IOException { - sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); + sut = new ReportController(csvWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); sut.report(Collections.singletonList(file)); + outputFile = new File(csvWriter.getFilename()); assertTrue(outputFile.exists(), "Output file missing!"); @@ -135,8 +164,9 @@ void testContent_METHOD_Decrypt() throws IOException { @Test void testContent_METHOD_Encrypt() throws IOException { - sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); + sut = new ReportController(csvWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); sut.report(Collections.singletonList(file)); + outputFile = new File(csvWriter.getFilename()); assertTrue(outputFile.exists(), "Output file missing!"); @@ -149,8 +179,9 @@ void testContent_METHOD_Encrypt() throws IOException { @Test void testConsistentColumns() throws IOException { - sut = new ReportController(resultsWriter, Collections.singletonList(ReportController.ReportGranularity.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!"); BufferedReader content = new BufferedReader(new FileReader(outputFile)); diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java index 7e99fab..ad1ef69 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerTest.java @@ -16,15 +16,15 @@ public class ReportControllerTest { AbstractSmell smell; List files; ReportController sut; - ResultsWriter resultsWriter; + CSVWriter csvWriter; @BeforeEach public void setUp() throws IOException { files = new ArrayList<>(); smell = mock(AbstractSmell.class); tf = mock(TestFile.class); - resultsWriter = mock(ResultsWriter.class); - sut = ReportController.createReportController(resultsWriter); + csvWriter = mock(CSVWriter.class); + sut = ReportController.createReportController(csvWriter); } @Test @@ -35,7 +35,7 @@ public void testReportDoesNotThrow() { @Test public void testReportNotCallExportSmells() throws IOException { sut.report(files); - verify(resultsWriter, never()).exportSmells((SmellsContainer) any()); + verify(csvWriter, never()).exportSmells((SmellsContainer) any()); } @Test @@ -45,7 +45,7 @@ public void testReportCallExportSmellsFromFile() throws IOException { sut.report(files); - verify(resultsWriter, atLeastOnce()).exportSmells(tf); + verify(csvWriter, atLeastOnce()).exportSmells(tf); } @Test @@ -57,7 +57,7 @@ public void testReportCallExportSmells() throws IOException { sut.report(files); - verify(resultsWriter, atLeastOnce()).exportSmells(tf); + verify(csvWriter, atLeastOnce()).exportSmells(tf); //verify(resultsWriter, atLeastOnce()).exportSmells(smellyElem); TODO: make exportSmells(smellyElem) be detectable } } From 3723487827f313bbb9ce448dca98f264632953e5 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Mon, 28 Sep 2020 11:09:45 -0300 Subject: [PATCH 29/42] fixed ConstructorInitializationIntegrationTest, added class granularity tests and missing Queue (smells-free) project resource --- .../testsmell/ReportController.java | 1 - src/resources/Queue/pom.xml | 110 ++++++++++ .../ufmg/aserg/victorveloso/queue/Queue.java | 198 ++++++++++++++++++ .../aserg/victorveloso/queue/QueueTest.java | 108 ++++++++++ .../ReportControllerIntegrationTest.java | 63 +++++- .../TestSmellDetectorIntegrationTest.java | 52 ++--- ...structorInitializationIntegrationTest.java | 2 +- 7 files changed, 498 insertions(+), 36 deletions(-) create mode 100644 src/resources/Queue/pom.xml create mode 100644 src/resources/Queue/src/main/java/br/ufmg/aserg/victorveloso/queue/Queue.java create mode 100644 src/resources/Queue/src/test/java/br/ufmg/aserg/victorveloso/queue/QueueTest.java diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java index f76066d..c54d9c1 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java @@ -72,7 +72,6 @@ private void reportTestFiles(List files) throws IOException { } private List mergeSmellyElements(List smells, Class type) { - return extractor.extract(smells, type); } 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/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java index 4259d82..3adfa00 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java @@ -44,7 +44,7 @@ void tearDown() { } @Test - void testMultipleOutput() throws IOException { + void testMultipleOutput_METHOD() throws IOException { List outputs = Arrays.asList(ReportController.ReportGranularity.METHOD, ReportController.ReportGranularity.CLASS, ReportController.ReportGranularity.FILE); sut = new ReportController(csvWriter, outputs); sut.report(Collections.singletonList(file)); @@ -53,15 +53,30 @@ void testMultipleOutput() throws IOException { 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 { + List outputs = Arrays.asList(ReportController.ReportGranularity.METHOD, ReportController.ReportGranularity.CLASS, ReportController.ReportGranularity.FILE); + sut = new ReportController(csvWriter, outputs); + sut.report(Collections.singletonList(new TestFile("ConstructorInitializationProject", "/ConstructorInitialization/src/test/java/org/briarproject/bramble/crypto/TagEncodingTest.java", ""))); outputFile = new File("CLASS_" + csvWriter.getSuffix()); assertTrue(outputFile.exists(), "Class output file missing!"); - 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()); // TODO: FIX class not being reported + List fileContent = new BufferedReader(new FileReader(outputFile)).lines().collect(Collectors.toList()); + assertEquals(0, 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 { + List outputs = Arrays.asList(ReportController.ReportGranularity.METHOD, ReportController.ReportGranularity.CLASS, ReportController.ReportGranularity.FILE); + sut = new ReportController(csvWriter, outputs); + sut.report(Collections.singletonList(file)); outputFile = new File("FILE_" + csvWriter.getSuffix()); assertTrue(outputFile.exists(), "File output file missing!"); - fileContent = new BufferedReader(new FileReader(outputFile)).lines().collect(Collectors.toList()); + 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()); @@ -191,3 +206,43 @@ void testConsistentColumns() throws IOException { assertEquals(nCols, 7 + smells.size()); } } + +class ReportControllerClassIntegrationTests { + 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/TestSmellDetectorIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIntegrationTest.java index 4598af8..2571177 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIntegrationTest.java @@ -3,7 +3,6 @@ import edu.rit.se.testsmells.testsmell.smell.*; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -17,34 +16,7 @@ public class TestSmellDetectorIntegrationTest { @BeforeEach void setUp() { - testSmellDetector = initializeSmells(); - } - - @AfterEach - void tearDown() { - testSmellDetector.clear(); - testSmellDetector = null; - } - - @Disabled - @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())); - } - } - } - - private TestSmellDetector initializeSmells() { - TestSmellDetector testSmellDetector = TestSmellDetector.createTestSmellDetector(); + testSmellDetector = TestSmellDetector.createTestSmellDetector(); testSmellDetector.addDetectableSmell(new AssertionRoulette()); testSmellDetector.addDetectableSmell(new ConditionalTestLogic()); @@ -68,7 +40,27 @@ private TestSmellDetector initializeSmells() { 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); - return testSmellDetector; + 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/smell/ConstructorInitializationIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitializationIntegrationTest.java index 3e57acd..eade4cf 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitializationIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitializationIntegrationTest.java @@ -43,7 +43,7 @@ public void testConstructorInitialization() throws IOException { boolean expectedSmellDetected = false; for (AbstractSmell testSmell : file.getTestSmells()) { if (testSmell != null) { - if (testSmell.getSmellName().equals(new ConditionalTestLogic().getSmellName())) { + if (testSmell.getSmellName().equals(new ConstructorInitialization().getSmellName())) { expectedSmellDetected = testSmell.hasSmell(); } } From f17e98215d5b76fffa96d72de61272fa37a5ff1a Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Mon, 28 Sep 2020 11:38:05 -0300 Subject: [PATCH 30/42] ReportControllerIntegrationTests better organized (split in 3 classes) and removed outdated comment --- .../ReportControllerClassIntegrationTest.java | 59 ++++++++++++++ .../ReportControllerIntegrationTest.java | 78 ------------------- ...lerMultipleGranularityIntegrationTest.java | 76 ++++++++++++++++++ .../TestSmellDetectorIntegrationTest.java | 2 +- 4 files changed, 136 insertions(+), 79 deletions(-) create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerClassIntegrationTest.java create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMultipleGranularityIntegrationTest.java 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/ReportControllerIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java index 3adfa00..6d08354 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java @@ -43,45 +43,6 @@ void tearDown() { outputFile.delete(); } - @Test - void testMultipleOutput_METHOD() throws IOException { - List outputs = Arrays.asList(ReportController.ReportGranularity.METHOD, ReportController.ReportGranularity.CLASS, ReportController.ReportGranularity.FILE); - sut = new ReportController(csvWriter, outputs); - sut.report(Collections.singletonList(file)); - - outputFile = new File("METHOD_" + csvWriter.getSuffix()); - 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 { - List outputs = Arrays.asList(ReportController.ReportGranularity.METHOD, ReportController.ReportGranularity.CLASS, ReportController.ReportGranularity.FILE); - sut = new ReportController(csvWriter, outputs); - sut.report(Collections.singletonList(new TestFile("ConstructorInitializationProject", "/ConstructorInitialization/src/test/java/org/briarproject/bramble/crypto/TagEncodingTest.java", ""))); - - 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(0, 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 { - List outputs = Arrays.asList(ReportController.ReportGranularity.METHOD, ReportController.ReportGranularity.CLASS, ReportController.ReportGranularity.FILE); - sut = new ReportController(csvWriter, outputs); - sut.report(Collections.singletonList(file)); - - outputFile = new File("FILE_" + csvWriter.getSuffix()); - 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()); - - - } - @Test void testReport() { sut = new ReportController(csvWriter, Collections.singletonList(ReportController.ReportGranularity.FILE)); @@ -207,42 +168,3 @@ void testConsistentColumns() throws IOException { } } -class ReportControllerClassIntegrationTests { - 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/ReportControllerMultipleGranularityIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMultipleGranularityIntegrationTest.java new file mode 100644 index 0000000..676e515 --- /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(0, 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/TestSmellDetectorIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIntegrationTest.java index 2571177..f892576 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorIntegrationTest.java @@ -21,7 +21,7 @@ void setUp() { testSmellDetector.addDetectableSmell(new AssertionRoulette()); testSmellDetector.addDetectableSmell(new ConditionalTestLogic()); - testSmellDetector.addDetectableSmell(new ConstructorInitialization()); // Constructor Initialization gives false positives + testSmellDetector.addDetectableSmell(new ConstructorInitialization()); testSmellDetector.addDetectableSmell(new DefaultTest()); testSmellDetector.addDetectableSmell(new EmptyTest()); testSmellDetector.addDetectableSmell(new ExceptionCatchingThrowing()); From daecf808b17f2636ad3fc63f8adb5638cfb7271c Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Mon, 28 Sep 2020 11:47:59 -0300 Subject: [PATCH 31/42] Enhanced ReportControllerIntegrationTests organization even more! --- .../ReportControllerFileIntegrationTest.java | 95 +++++++++++++++++++ ...eportControllerMethodIntegrationTest.java} | 83 ++-------------- 2 files changed, 102 insertions(+), 76 deletions(-) create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerFileIntegrationTest.java rename src/test/java/edu/rit/se/testsmells/testsmell/{ReportControllerIntegrationTest.java => ReportControllerMethodIntegrationTest.java} (59%) 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/ReportControllerIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java similarity index 59% rename from src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java rename to src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java index 6d08354..3843230 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java @@ -15,10 +15,11 @@ import java.util.List; import java.util.stream.Collectors; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; @IntegrationTest -class ReportControllerIntegrationTest { +class ReportControllerMethodIntegrationTest { private File outputFile; private TestFile file; private ReportController sut; @@ -35,6 +36,9 @@ void setUp() throws IOException { 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 @@ -44,45 +48,8 @@ void tearDown() { } @Test - void testReport() { - 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 { - 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!"); - - 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 { - sut = new ReportController(csvWriter, Collections.singletonList(ReportController.ReportGranularity.FILE)); - sut.report(Collections.singletonList(file)); - outputFile = new File(csvWriter.getFilename()); + void testReportExists() { assertTrue(outputFile.exists(), "Output file missing!"); - - 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); } List getMethodSmells() { @@ -98,11 +65,6 @@ List getMethodSmells() { @Test void testReport_METHOD() throws IOException { - sut = new ReportController(csvWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); - sut.report(Collections.singletonList(file)); - outputFile = new File(csvWriter.getFilename()); - assertTrue(outputFile.exists(), "Output file missing!"); - List entries = new BufferedReader(new FileReader(outputFile)).lines().collect(Collectors.toList()); @@ -113,11 +75,6 @@ void testReport_METHOD() throws IOException { @Test void testHeader_METHOD() throws IOException { - sut = new ReportController(csvWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); - sut.report(Collections.singletonList(file)); - outputFile = new File(csvWriter.getFilename()); - assertTrue(outputFile.exists(), "Output file missing!"); - List headerEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).readLine().split(",")); assertEquals("Element Name,WhileCount,ConditionCount,RedundantCount,AssertCount,IfCount,ExceptionCount,ForeachCount,PrintCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,SensitiveCount,MagicNumberCount,Assertion Roulette,Eager Test,Mystery Guest,Sleepy Test,Unknown Test,Redundant Assertion,Magic Number Test,Conditional Test Logic,EmptyTest,General Fixture,Sensitive Equality,Verbose Test,Resource Optimism,Duplicate Assert,Exception Catching Throwing,Print Statement,Lazy Test", String.join(",", headerEntries)/*, String.join(",",expectedEntries)*/); @@ -125,12 +82,6 @@ void testHeader_METHOD() throws IOException { @Test void testContent_METHOD_Decrypt() throws IOException { - sut = new ReportController(csvWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); - sut.report(Collections.singletonList(file)); - outputFile = new File(csvWriter.getFilename()); - - assertTrue(outputFile.exists(), "Output file missing!"); - assertEquals(17, getMethodSmells().size()); List contentEntries = Arrays.asList(new BufferedReader(new FileReader(outputFile)).lines().skip(1).findFirst().orElse("").split(",")); @@ -140,31 +91,11 @@ void testContent_METHOD_Decrypt() throws IOException { @Test void testContent_METHOD_Encrypt() throws IOException { - sut = new ReportController(csvWriter, Collections.singletonList(ReportController.ReportGranularity.METHOD)); - sut.report(Collections.singletonList(file)); - outputFile = new File(csvWriter.getFilename()); - - assertTrue(outputFile.exists(), "Output file missing!"); - assertEquals(17, 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(14, contentEntries.stream().filter(e -> e.equals("false")).count()); // 17 methods smells - 3 detected smells } - - @Test - void testConsistentColumns() throws IOException { - 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!"); - - BufferedReader content = new BufferedReader(new FileReader(outputFile)); - int nCols = content.readLine().split(",").length; - - assertEquals(nCols, content.readLine().split(",").length); - assertEquals(nCols, 7 + smells.size()); - } } From c2a994cbf6be43a9f29016eb3ca04826b640a699 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Mon, 28 Sep 2020 11:59:31 -0300 Subject: [PATCH 32/42] Make all granularities enabled by default and updated project version. --- pom.xml | 2 +- src/main/java/edu/rit/se/testsmells/Main.java | 9 +-------- src/resources/test-smells.properties | 2 +- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index 0e1b7a8..b83a76d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 edu.rit.se.testsmells TestSmellDetector - 0.1 + 0.2 jar edu.rit.se.testsmells.Main diff --git a/src/main/java/edu/rit/se/testsmells/Main.java b/src/main/java/edu/rit/se/testsmells/Main.java index 252229c..2195d21 100644 --- a/src/main/java/edu/rit/se/testsmells/Main.java +++ b/src/main/java/edu/rit/se/testsmells/Main.java @@ -22,7 +22,7 @@ public static void main(String[] args) throws IOException { TestSmellDetector testSmellDetector = initializeSmells(); List files = readInputTestFiles(inputFile); - CSVWriter csvWriter = initializeOutputFile(testSmellDetector); + CSVWriter csvWriter = CSVWriter.createResultsWriter(); ReportController reportCtrl = ReportController.createReportController(csvWriter); for (TestFile file : files) { @@ -39,13 +39,6 @@ private static Object getCurrentDateFormatted() { return (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()); } - private static CSVWriter initializeOutputFile(TestSmellDetector testSmellDetector) throws IOException { - -// resultsWriter.writeCSVHeader(testSmellDetector, anyFile); - - return CSVWriter.createResultsWriter(); - } - private static List readInputTestFiles(File inputFile) throws IOException { BufferedReader in = new BufferedReader(new FileReader(inputFile)); String str; diff --git a/src/resources/test-smells.properties b/src/resources/test-smells.properties index cf28140..d980f6d 100644 --- a/src/resources/test-smells.properties +++ b/src/resources/test-smells.properties @@ -1 +1 @@ -report.granularity=FILE \ No newline at end of file +report.granularity=FILE,CLASS,METHOD \ No newline at end of file From d972bd3ebb19c42d3f900b520c00d47476ff2e11 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Sat, 24 Oct 2020 23:32:18 -0300 Subject: [PATCH 33/42] Fixed outputting header repeatedly for each inputted TestFile; Optimized code (Smells and SmellyElements collections are internally treated as Set, but returned as list): simplified "contains" method and "upsert" operation. --- .../testsmells/testsmell/AbstractSmell.java | 10 +- .../se/testsmells/testsmell/CSVWriter.java | 6 +- .../testsmell/ReportController.java | 21 ++- .../testsmells/testsmell/SmellsContainer.java | 28 +--- .../testsmell/TestSmellDetector.java | 4 +- .../testsmell/MultiFilesIntegrationTest.java | 139 ++++++++++++++++++ 6 files changed, 170 insertions(+), 38 deletions(-) create mode 100644 src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java index 15e5f04..ab0072b 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java @@ -9,18 +9,21 @@ 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.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; public abstract class AbstractSmell { private final MethodValidator methodValidator; - private final List smellyElementList; + private final Set smellyElementList; public abstract String getSmellName(); public AbstractSmell() { methodValidator = MethodValidator.getInstance(); - smellyElementList = new CopyOnWriteArrayList<>(); + smellyElementList = new CopyOnWriteArraySet<>(); } protected String getFullMethodName(CompilationUnit unit, T node) { @@ -51,11 +54,12 @@ public void clear() { * Returns the set of analyzed elements (i.e. test methods) */ public List getSmellyElements() { - return smellyElementList; + return new ArrayList<>(smellyElementList); } public void addSmellyElement(SmellyElement elem) { smellyElementList.add(elem); + elem.addDetectedSmell(this); } /** diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/CSVWriter.java b/src/main/java/edu/rit/se/testsmells/testsmell/CSVWriter.java index ef2932e..657322d 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/CSVWriter.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/CSVWriter.java @@ -50,19 +50,18 @@ String getFilename() { return name; } - private void writeCSVHeader(List reports) throws IOException { + public void writeCSVHeader(List reports) throws IOException { headers = reports.stream().flatMap(report -> report.getEntryKeys().stream()).distinct().collect(Collectors.toList()); writeCSV(headers); } - private void writeCSVHeader(SmellsContainer anyFile) throws IOException { + 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 { - writeCSVHeader(reports); for (Report report : reports) { List entries = new ArrayList<>(); for (String column : headers) { @@ -73,7 +72,6 @@ void exportSmells(List reports) throws IOException { } void exportSmells(SmellsContainer fileTestSmells) throws IOException { - writeCSVHeader(fileTestSmells); List entries = new ArrayList<>(fileTestSmells.getTestDescriptionEntries().values()); for (AbstractSmell smell : fileTestSmells.getTestSmells()) { try { diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java index c54d9c1..3d04dc9 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/ReportController.java @@ -54,20 +54,29 @@ public void report(List files) throws IOException { } private void reportTestMethods(List files) throws IOException { - for (TestFile file : files) { - reportSmellyElements(file.getTestSmells(), TestMethod.class); + 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 { - for (TestFile file : files) { - reportSmellyElements(file.getTestSmells(), TestClass.class); + 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 { - for (TestFile file : files) { - csvWriter.exportSmells(file); + if (files.size() > 0) { + csvWriter.writeCSVHeader(files.get(0)); + for (TestFile file : files) { + csvWriter.exportSmells(file); + } } } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java b/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java index 39ed95e..98ba7e5 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java @@ -1,37 +1,21 @@ package edu.rit.se.testsmells.testsmell; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.stream.Collectors; public interface SmellsContainer { - List testSmells = new CopyOnWriteArrayList<>(); + Set testSmells = new CopyOnWriteArraySet<>(); Map getTestDescriptionEntries(); default void addDetectedSmell(AbstractSmell newSmell) { assert Objects.nonNull(newSmell); - AbstractSmell existingSmell = findSmellNamed(newSmell.getSmellName()); - if (Objects.nonNull(existingSmell)) { - for (SmellyElement smellyElement : newSmell.getSmellyElements()) { - existingSmell.addSmellyElement(smellyElement); - } - } else { - testSmells.add(newSmell); - } - } - - default AbstractSmell findSmellNamed(String smellName) { - for (AbstractSmell existingSmell : testSmells) { - if (smellName.equals(existingSmell.getSmellName())) { - return existingSmell; - } - } - return null; + testSmells.add(newSmell); } default List getTestSmells() { - return testSmells; + return new ArrayList<>(testSmells); } } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java index 0f8fe11..ab3bb33 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java @@ -77,9 +77,7 @@ public void detectSmells(TestFile testFile) throws IOException { } testFile.addDetectedSmell(smell); - for (SmellsContainer element : smell.getSmellyElements()) { - element.addDetectedSmell(smell); - } + //smell.clear(); TODO: Old files analysis interfering on current. It must clear smells, but after report! } } 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..239e5b8 --- /dev/null +++ b/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java @@ -0,0 +1,139 @@ +package edu.rit.se.testsmells.testsmell; + +import edu.rit.se.testsmells.testsmell.smell.*; +import org.junit.Ignore; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +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.*; + +@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 testBothDuplicateAssertAndRedundantPrint() throws IOException { + CSVWriter csvWriter = CSVWriter.createResultsWriter(); + ReportController reportCtrl = ReportController.createReportController(csvWriter); + + 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", + "" + )); + 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()); + + // Method, class, and file should not be filtered + assertEquals(reportsSize.size(), 3); + // 1 DuplicateAssert test case + 7 RedundantPrint test case + header + assertEquals(reportsSize.get(0), 9); + // 1 DuplicateAssert test class + 1 RedundantPrint test class + header + assertEquals(reportsSize.get(1), 3); + // 1 DuplicateAssert test file + 1 RedundantPrint test file + header + assertEquals(reportsSize.get(2), 3); + } + + @Disabled + @Test + public void testZxingSmells() throws IOException { + String inputFile = "/zxing/UniqueTestFiles.txt"; + 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); + } + for (TestFile file : testFiles) { + testSmellDetector.detectSmells(file); + } + } +} From 15d7a5d653e81a61bee39d244e0780c26b239863 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Sun, 25 Oct 2020 03:18:33 -0300 Subject: [PATCH 34/42] Fixed multi-file repeated report entries: - Isolating AbstractSmell instances per TestFile - SmellsContainer turned into a class and testSmells list made non-static --- .../rit/se/testsmells/testsmell/AbstractSmell.java | 2 ++ .../rit/se/testsmells/testsmell/SmellsContainer.java | 10 +++++----- .../rit/se/testsmells/testsmell/SmellyElement.java | 2 +- .../edu/rit/se/testsmells/testsmell/TestFile.java | 2 +- .../se/testsmells/testsmell/TestSmellDetector.java | 6 +++--- .../testsmell/smell/AssertionRoulette.java | 5 +++++ .../testsmell/smell/ConditionalTestLogic.java | 5 +++++ .../testsmell/smell/ConstructorInitialization.java | 5 +++++ .../se/testsmells/testsmell/smell/DefaultTest.java | 5 +++++ .../se/testsmells/testsmell/smell/DependentTest.java | 5 +++++ .../testsmells/testsmell/smell/DuplicateAssert.java | 9 +++++++-- .../rit/se/testsmells/testsmell/smell/EagerTest.java | 5 +++++ .../rit/se/testsmells/testsmell/smell/EmptyTest.java | 5 +++++ .../testsmell/smell/ExceptionCatchingThrowing.java | 5 +++++ .../testsmells/testsmell/smell/GeneralFixture.java | 5 +++++ .../se/testsmells/testsmell/smell/IgnoredTest.java | 5 +++++ .../rit/se/testsmells/testsmell/smell/LazyTest.java | 5 +++++ .../testsmells/testsmell/smell/MagicNumberTest.java | 5 +++++ .../se/testsmells/testsmell/smell/MysteryGuest.java | 5 +++++ .../testsmells/testsmell/smell/PrintStatement.java | 5 +++++ .../testsmell/smell/RedundantAssertion.java | 5 +++++ .../testsmells/testsmell/smell/ResourceOptimism.java | 5 +++++ .../testsmell/smell/SensitiveEquality.java | 5 +++++ .../se/testsmells/testsmell/smell/SleepyTest.java | 5 +++++ .../se/testsmells/testsmell/smell/UnknownTest.java | 5 +++++ .../se/testsmells/testsmell/smell/VerboseTest.java | 5 +++++ .../testsmell/MultiFilesIntegrationTest.java | 12 ++++++------ .../testsmells/testsmell/TestSmellDetectorTest.java | 7 ++++++- 28 files changed, 131 insertions(+), 19 deletions(-) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java index ab0072b..8879037 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java @@ -26,6 +26,8 @@ public AbstractSmell() { smellyElementList = new CopyOnWriteArraySet<>(); } + public abstract AbstractSmell recreate(); + protected String getFullMethodName(CompilationUnit unit, T node) { String className = node.getParentNode().map(x -> (ClassOrInterfaceDeclaration) x).map(NodeWithSimpleName::getNameAsString).orElse(""); diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java b/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java index 98ba7e5..fd74911 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/SmellsContainer.java @@ -5,17 +5,17 @@ import java.util.concurrent.CopyOnWriteArraySet; import java.util.stream.Collectors; -public interface SmellsContainer { - Set testSmells = new CopyOnWriteArraySet<>(); +public abstract class SmellsContainer { + public final Set testSmells = new CopyOnWriteArraySet<>(); - Map getTestDescriptionEntries(); + public abstract Map getTestDescriptionEntries(); - default void addDetectedSmell(AbstractSmell newSmell) { + public void addDetectedSmell(AbstractSmell newSmell) { assert Objects.nonNull(newSmell); testSmells.add(newSmell); } - default List getTestSmells() { + public List getTestSmells() { return new ArrayList<>(testSmells); } } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/SmellyElement.java b/src/main/java/edu/rit/se/testsmells/testsmell/SmellyElement.java index 2279d63..dc64f68 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/SmellyElement.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/SmellyElement.java @@ -3,7 +3,7 @@ import java.util.HashMap; import java.util.Map; -public abstract class SmellyElement implements SmellsContainer { +public abstract class SmellyElement extends SmellsContainer { private final String name; private final Map data; private boolean hasSmell; diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java index ac90ec0..4b2c445 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestFile.java @@ -4,7 +4,7 @@ import java.util.HashMap; import java.util.Map; -public class TestFile implements SmellsContainer { +public class TestFile extends SmellsContainer { private final String app, testFilePath, productionFilePath; private boolean isProductionFileOmitted = false; diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java index ab3bb33..d4baa27 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java @@ -65,19 +65,19 @@ public List getTestSmellNames() { /** * 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) throws IOException { + public void detectSmells(TestFile testFile) { CompilationUnit testFileCompilationUnit = parseIntoCompilationUnit(testFile.getTestFilePath()); CompilationUnit productionFileCompilationUnit = parseIntoCompilationUnit(testFile.getProductionFilePath()); - for (AbstractSmell smell : testSmells) { + for (AbstractSmell smellPrototype : testSmells) { + AbstractSmell smell = smellPrototype.recreate(); try { smell.runAnalysis(testFileCompilationUnit, productionFileCompilationUnit, testFile.getTestFileNameWithoutExtension(), testFile.getProductionFileNameWithoutExtension()); } catch (FileNotFoundException ignored) { } testFile.addDetectedSmell(smell); - //smell.clear(); TODO: Old files analysis interfering on current. It must clear smells, but after report! } } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java index a49d5c2..a476f15 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java @@ -23,6 +23,11 @@ public AssertionRoulette() { super(); } + @Override + public AbstractSmell recreate() { + return new AssertionRoulette(); + } + /** * Checks of 'Assertion Roulette' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java index 069dfa5..df5eb48 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConditionalTestLogic.java @@ -21,6 +21,11 @@ public ConditionalTestLogic() { super(); } + @Override + public AbstractSmell recreate() { + return new ConditionalTestLogic(); + } + /** * Checks of 'Conditional Test Logic' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java index 94b4e65..31e41b1 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java @@ -23,6 +23,11 @@ public ConstructorInitialization() { super(); } + @Override + public AbstractSmell recreate() { + return new ConstructorInitialization(); + } + /** * Checks of 'Constructor Initialization' smell */ 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 index fe777a2..728c898 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DefaultTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DefaultTest.java @@ -21,6 +21,11 @@ public DefaultTest() { super(); } + @Override + public AbstractSmell recreate() { + return new DefaultTest(); + } + /** * Checks of 'Default Test' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java index 1c7fc87..af7714e 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java @@ -21,6 +21,11 @@ public DependentTest() { testMethods = new ArrayList<>(); } + @Override + public AbstractSmell recreate() { + return new DependentTest(); + } + /** * Checks of 'DependentTest' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java index 2ae67a7..4a29642 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DuplicateAssert.java @@ -22,6 +22,11 @@ public DuplicateAssert() { super(); } + @Override + public AbstractSmell recreate() { + return new DuplicateAssert(); + } + /** * Checks of 'Duplicate Assert' smell */ @@ -58,13 +63,13 @@ public void visit(MethodDeclaration n, Void arg) { 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); } diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java index 4cf5709..bdad57f 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EagerTest.java @@ -32,6 +32,11 @@ public EagerTest() { productionMethods = new ArrayList<>(); } + @Override + public AbstractSmell recreate() { + return new EagerTest(); + } + /** * Checks of 'Eager Test' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java index 04183b3..5c3dc6f 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/EmptyTest.java @@ -21,6 +21,11 @@ public EmptyTest() { super(); } + @Override + public AbstractSmell recreate() { + return new EmptyTest(); + } + /** * Checks of 'Empty Test' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java index f60c550..61079bc 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ExceptionCatchingThrowing.java @@ -23,6 +23,11 @@ public ExceptionCatchingThrowing() { super(); } + @Override + public AbstractSmell recreate() { + return new ExceptionCatchingThrowing(); + } + /** * Checks of 'Exception Catching Throwing' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java index e42f033..df07f89 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/GeneralFixture.java @@ -33,6 +33,11 @@ public GeneralFixture() { setupFields = new ArrayList<>(); } + @Override + public AbstractSmell recreate() { + return new GeneralFixture(); + } + /** * Checks of 'General Fixture' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java index 9c19133..a98db70 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java @@ -20,6 +20,11 @@ public IgnoredTest() { super(); } + @Override + public AbstractSmell recreate() { + return new IgnoredTest(); + } + /** * Checks of 'Ignored Test' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java index fa65272..2c0a9ca 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/LazyTest.java @@ -33,6 +33,11 @@ public LazyTest() { calledProductionMethods = new ArrayList<>(); } + @Override + public AbstractSmell recreate() { + return new LazyTest(); + } + /** * Checks of 'Lazy Test' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java index 9d475e4..15615540 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MagicNumberTest.java @@ -20,6 +20,11 @@ public MagicNumberTest() { super(); } + @Override + public AbstractSmell recreate() { + return new MagicNumberTest(); + } + /** * Checks of 'MagicNumberTest' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java index 4d4b492..87a0733 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/MysteryGuest.java @@ -29,6 +29,11 @@ public MysteryGuest() { super(); } + @Override + public AbstractSmell recreate() { + return new MysteryGuest(); + } + /** * Checks of 'Mystery Guest' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java index 92a4df4..c5e8af2 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/PrintStatement.java @@ -24,6 +24,11 @@ public PrintStatement() { super(); } + @Override + public AbstractSmell recreate() { + return new PrintStatement(); + } + /** * Checks of 'Print Statement' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java index 65a9548..baed054 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/RedundantAssertion.java @@ -23,6 +23,11 @@ public RedundantAssertion() { super(); } + @Override + public AbstractSmell recreate() { + return new RedundantAssertion(); + } + /** * Checks of 'Redundant Assertion' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java index 4809624..20a47b0 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ResourceOptimism.java @@ -25,6 +25,11 @@ public ResourceOptimism() { super(); } + @Override + public AbstractSmell recreate() { + return new ResourceOptimism(); + } + /** * Checks of 'Resource Optimism' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java index 1634da2..540c9a1 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SensitiveEquality.java @@ -19,6 +19,11 @@ public SensitiveEquality() { super(); } + @Override + public AbstractSmell recreate() { + return new SensitiveEquality(); + } + /** * Checks of 'Sensitive Equality' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java index fec2a6f..cc5a44b 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/SleepyTest.java @@ -23,6 +23,11 @@ public SleepyTest() { super(); } + @Override + public AbstractSmell recreate() { + return new SleepyTest(); + } + /** * Checks of 'SleepyTest' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java index 2ece260..8896936 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/UnknownTest.java @@ -24,6 +24,11 @@ public UnknownTest() { super(); } + @Override + public AbstractSmell recreate() { + return new UnknownTest(); + } + /** * Checks of 'Unknown Test' smell */ diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java index 1e4e04a..08cca79 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/VerboseTest.java @@ -20,6 +20,11 @@ public VerboseTest() { super(); } + @Override + public AbstractSmell recreate() { + return new VerboseTest(); + } + /** * Checks of 'Verbose Test' smell */ diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java index 239e5b8..f19c792 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java @@ -1,7 +1,6 @@ package edu.rit.se.testsmells.testsmell; import edu.rit.se.testsmells.testsmell.smell.*; -import org.junit.Ignore; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; @@ -13,7 +12,8 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.*; + +import static org.junit.jupiter.api.Assertions.assertEquals; @IntegrationTest public class MultiFilesIntegrationTest { @@ -100,13 +100,13 @@ public void testBothDuplicateAssertAndRedundantPrint() throws IOException { .collect(Collectors.toList()); // Method, class, and file should not be filtered - assertEquals(reportsSize.size(), 3); + assertEquals(3, reportsSize.size()); // 1 DuplicateAssert test case + 7 RedundantPrint test case + header - assertEquals(reportsSize.get(0), 9); + assertEquals(9, reportsSize.get(0)); // 1 DuplicateAssert test class + 1 RedundantPrint test class + header - assertEquals(reportsSize.get(1), 3); + assertEquals(0, reportsSize.get(1)); // 1 DuplicateAssert test file + 1 RedundantPrint test file + header - assertEquals(reportsSize.get(2), 3); + assertEquals(3, reportsSize.get(2)); } @Disabled diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java index 6545f78..2c67176 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/TestSmellDetectorTest.java @@ -36,7 +36,7 @@ void getTestSmellNames() { @Test void detectSmells() throws IOException { TestFile tf = new TestFileStub("app", "", ""); - List smells = new ArrayList<>(); + List smells; this.sut.detectSmells(tf); smells = tf.getTestSmells(); @@ -72,6 +72,11 @@ 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"); From 307339523e28682e55f69c5ad9f405ab720a0684 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Mon, 26 Oct 2020 20:37:22 -0300 Subject: [PATCH 35/42] Implemented failing tests for class granularity report and fixed DependentTest omission on failure --- .../testsmell/smell/DependentTest.java | 12 +- .../testsmell/MultiFilesIntegrationTest.java | 149 ++++++++++++------ ...ReportControllerMethodIntegrationTest.java | 16 +- 3 files changed, 123 insertions(+), 54 deletions(-) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java index af7714e..014dff6 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DependentTest.java @@ -5,6 +5,7 @@ import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import edu.rit.se.testsmells.testsmell.AbstractSmell; +import edu.rit.se.testsmells.testsmell.TestMethod; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -44,9 +45,16 @@ 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())))){ - addSmellyElement(new edu.rit.se.testsmells.testsmell.TestMethod(getFullMethodName(testFileCompilationUnit, testMethod.getMethodDeclaration()))); + 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); + } + } } + addSmellyElement(smell); } } diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java index f19c792..b782f99 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java @@ -3,7 +3,6 @@ import edu.rit.se.testsmells.testsmell.smell.*; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.io.*; @@ -13,7 +12,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.*; @IntegrationTest public class MultiFilesIntegrationTest { @@ -28,7 +27,6 @@ void setUp() { testSmellDetector.addDetectableSmell(new AssertionRoulette()); testSmellDetector.addDetectableSmell(new ConditionalTestLogic()); - testSmellDetector.addDetectableSmell(new ConstructorInitialization()); testSmellDetector.addDetectableSmell(new DefaultTest()); testSmellDetector.addDetectableSmell(new EmptyTest()); @@ -58,10 +56,7 @@ void tearDown() { } @Test - public void testBothDuplicateAssertAndRedundantPrint() throws IOException { - CSVWriter csvWriter = CSVWriter.createResultsWriter(); - ReportController reportCtrl = ReportController.createReportController(csvWriter); - + public void testTwoMethodOrientedSmells() throws IOException { List testFiles = Arrays.asList( new TestFile( "RedundantPrintProject", @@ -73,6 +68,108 @@ public void testBothDuplicateAssertAndRedundantPrint() throws IOException { "/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)); // TODO: FIX Empty file being created + // 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)); + // 1 DefaultTest test class + 1 ConstructorInitialization test class + header + assertEquals(3, reportsSize.get(1)); //TODO: FIX ConstructorInitialization column missing (CLASS report) + // 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)); + // 1 DefaultTest test class + 1 DuplicateAssert test class + header + assertEquals(3, reportsSize.get(1)); // TODO: FIX DuplicateAssert entry missing (CLASS report) + // 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,AssertCount,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,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,Default Test,Constructor Initialization,IgnoredTest"; + + 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); } @@ -98,42 +195,6 @@ public void testBothDuplicateAssertAndRedundantPrint() throws IOException { } }) .collect(Collectors.toList()); - - // 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(0, reportsSize.get(1)); - // 1 DuplicateAssert test file + 1 RedundantPrint test file + header - assertEquals(3, reportsSize.get(2)); - } - - @Disabled - @Test - public void testZxingSmells() throws IOException { - String inputFile = "/zxing/UniqueTestFiles.txt"; - 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); - } - for (TestFile file : testFiles) { - testSmellDetector.detectSmells(file); - } + return reportsSize; } } diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java index 3843230..659035b 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java @@ -68,34 +68,34 @@ void testReport_METHOD() throws IOException { List entries = new BufferedReader(new FileReader(outputFile)).lines().collect(Collectors.toList()); - assertEquals("Element Name,WhileCount,ConditionCount,RedundantCount,AssertCount,IfCount,ExceptionCount,ForeachCount,PrintCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,SensitiveCount,MagicNumberCount,Assertion Roulette,Eager Test,Mystery Guest,Sleepy Test,Unknown Test,Redundant Assertion,Magic Number Test,Conditional Test Logic,EmptyTest,General Fixture,Sensitive Equality,Verbose Test,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,0,1,0,0,0,0,0,0,0,0,0,0,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,1,0,1,0,0,0,0,0,0,0,0,0,0,false,true,false,false,false,false,false,false,false,false,false,false,false,false,true,false,true", entries.get(2)); + assertEquals("Element Name,WhileCount,ConditionCount,RedundantCount,AssertCount,IfCount,ExceptionCount,ForeachCount,PrintCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,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,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,0,1,0,0,0,0,0,0,0,0,0,0,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,1,0,1,0,0,0,0,0,0,0,0,0,0,false,true,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,AssertCount,IfCount,ExceptionCount,ForeachCount,PrintCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,SensitiveCount,MagicNumberCount,Assertion Roulette,Eager Test,Mystery Guest,Sleepy Test,Unknown Test,Redundant Assertion,Magic Number Test,Conditional Test Logic,EmptyTest,General Fixture,Sensitive Equality,Verbose Test,Resource Optimism,Duplicate Assert,Exception Catching Throwing,Print Statement,Lazy Test", String.join(",", headerEntries)/*, String.join(",",expectedEntries)*/); + assertEquals("Element Name,WhileCount,ConditionCount,RedundantCount,AssertCount,IfCount,ExceptionCount,ForeachCount,PrintCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,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,Resource Optimism,Duplicate Assert,Exception Catching Throwing,Print Statement,Lazy Test", String.join(",", headerEntries)/*, String.join(",",expectedEntries)*/); } @Test void testContent_METHOD_Decrypt() throws IOException { - assertEquals(17, getMethodSmells().size()); + assertEquals(18, 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(15, contentEntries.stream().filter(e -> e.equals("false")).count()); // 17 methods smells - 2 detected smells + assertEquals(16, contentEntries.stream().filter(e -> e.equals("false")).count()); // 18 methods smells - 2 detected smells } @Test void testContent_METHOD_Encrypt() throws IOException { - assertEquals(17, getMethodSmells().size()); + assertEquals(18, 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(14, contentEntries.stream().filter(e -> e.equals("false")).count()); // 17 methods smells - 3 detected smells + assertEquals(15, contentEntries.stream().filter(e -> e.equals("false")).count()); // 18 methods smells - 3 detected smells } } From 9d11c3928c1c6c8946ca112c16ab1e3d610244ad Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Mon, 26 Oct 2020 21:36:28 -0300 Subject: [PATCH 36/42] Fixed ConstructorInitialization omission on failure --- .../smell/ConstructorInitialization.java | 19 ++++++++++--------- ...lerMultipleGranularityIntegrationTest.java | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java index 31e41b1..a053f2b 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java @@ -9,6 +9,8 @@ import edu.rit.se.testsmells.testsmell.TestClass; import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.List; /* @@ -40,7 +42,7 @@ public String getSmellName() { * 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(); @@ -51,26 +53,25 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit,CompilationUnit 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;i fileContent = new BufferedReader(new FileReader(outputFile)).lines().collect(Collectors.toList()); - assertEquals(0, fileContent.size(), "File with unexpected size received! File's content is: " + System.lineSeparator() + String.join(System.lineSeparator(), fileContent) + System.lineSeparator()); + assertEquals(2, fileContent.size(), "File with unexpected size received! File's content is: " + System.lineSeparator() + String.join(System.lineSeparator(), fileContent) + System.lineSeparator()); } From d3d27a78a082c20a60af61259126aaed46ea70d7 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Mon, 26 Oct 2020 22:30:49 -0300 Subject: [PATCH 37/42] Fixed DefaultTest and IgnoredTest omission/null on failure; added support for JUnit5 @Disabled annotation; updated tests --- .../smell/ConstructorInitialization.java | 7 ++-- .../testsmell/smell/DefaultTest.java | 9 ++--- .../testsmell/smell/IgnoredTest.java | 36 ++++++++----------- .../testsmell/MultiFilesIntegrationTest.java | 14 ++++---- ...ReportControllerMethodIntegrationTest.java | 16 ++++----- 5 files changed, 34 insertions(+), 48 deletions(-) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java index a053f2b..08f284d 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/ConstructorInitialization.java @@ -57,14 +57,11 @@ private class ClassVisitor extends VoidVisitorAdapter { @Override public void visit(ClassOrInterfaceDeclaration n, Void arg) { - for (int i = 0; i < n.getExtendedTypes().size(); i++) { - ClassOrInterfaceType node = n.getExtendedTypes().get(i); - constructorAllowed = node.getNameAsString().equals("ActivityInstrumentationTestCase2"); - } + constructorAllowed = n.getExtendedTypes().stream().anyMatch(c->c.getNameAsString().equals("ActivityInstrumentationTestCase2")); testClass = new TestClass(getFullClassName(testFileCompilationUnit, n)); testClass.setHasSmell(false); - super.visit(n, arg); addSmellyElement(testClass); + super.visit(n, arg); } @Override 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 index 728c898..b2e790b 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/DefaultTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/DefaultTest.java @@ -43,15 +43,12 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit } 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(getFullClassName(testFileCompilationUnit, n)); - testClass.setHasSmell(true); - addSmellyElement(testClass); - } + 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/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java index a98db70..002361d 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/IgnoredTest.java @@ -4,6 +4,7 @@ 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 edu.rit.se.testsmells.testsmell.AbstractSmell; import edu.rit.se.testsmells.testsmell.TestClass; @@ -44,6 +45,9 @@ public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit classVisitor.visit(this.testFileCompilationUnit, null); } + private boolean isIgnored(NodeWithAnnotations n) { + return n.getAnnotationByName("Ignore").isPresent() || n.getAnnotationByName("Disabled").isPresent(); + } /** * Visitor class */ @@ -56,11 +60,9 @@ private class ClassVisitor extends VoidVisitorAdapter { */ @Override public void visit(ClassOrInterfaceDeclaration n, Void arg) { - if (n.getAnnotationByName("Ignore").isPresent()) { - testClass = new TestClass(getFullClassName(testFileCompilationUnit, n)); - testClass.setHasSmell(true); - addSmellyElement(testClass); - } + testClass = new TestClass(getFullClassName(testFileCompilationUnit, n)); + testClass.setHasSmell(isIgnored(n)); + addSmellyElement(testClass); super.visit(n, arg); } @@ -69,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(getFullMethodName(testFileCompilationUnit, n)); - testMethod.setHasSmell(true); - addSmellyElement(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(getFullMethodName(testFileCompilationUnit, n)); - testMethod.setHasSmell(true); - addSmellyElement(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/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java index b782f99..f4b36bf 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java @@ -76,7 +76,7 @@ public void testTwoMethodOrientedSmells() throws IOException { // 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)); // TODO: FIX Empty file being created + assertEquals(3, reportsSize.get(1)); // 1 DuplicateAssert test file + 1 RedundantPrint test file + header assertEquals(3, reportsSize.get(2)); } @@ -101,8 +101,8 @@ public void testTwoClassOrientedSmells() throws IOException { assertEquals(3, reportsSize.size()); // 1 DuplicateAssert test case + 7 RedundantPrint test case + header assertEquals(6, reportsSize.get(0)); - // 1 DefaultTest test class + 1 ConstructorInitialization test class + header - assertEquals(3, reportsSize.get(1)); //TODO: FIX ConstructorInitialization column missing (CLASS report) + // 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)); } @@ -127,8 +127,8 @@ public void testBothClassAndMethodOrientedSmells() throws IOException { assertEquals(3, reportsSize.size()); // 1 DuplicateAssert test case + 7 RedundantPrint test case + header assertEquals(4, reportsSize.get(0)); - // 1 DefaultTest test class + 1 DuplicateAssert test class + header - assertEquals(3, reportsSize.get(1)); // TODO: FIX DuplicateAssert entry missing (CLASS report) + // 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)); } @@ -151,9 +151,9 @@ public void testBothClassAndMethodOrientedSmells_csvColumns() throws IOException assertEquals(3, outputFiles.size(), "Not generated all three reports"); - String expectedMethodHeader = "Element Name,WhileCount,ConditionCount,RedundantCount,AssertCount,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,Resource Optimism,Duplicate Assert,Exception Catching Throwing,Print Statement"; + String expectedMethodHeader = "Element Name,WhileCount,ConditionCount,RedundantCount,AssertCount,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,Default Test,Constructor Initialization,IgnoredTest"; + 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()); diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java index 659035b..73d015f 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java @@ -68,34 +68,34 @@ void testReport_METHOD() throws IOException { List entries = new BufferedReader(new FileReader(outputFile)).lines().collect(Collectors.toList()); - assertEquals("Element Name,WhileCount,ConditionCount,RedundantCount,AssertCount,IfCount,ExceptionCount,ForeachCount,PrintCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,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,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,0,1,0,0,0,0,0,0,0,0,0,0,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,1,0,1,0,0,0,0,0,0,0,0,0,0,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,true", entries.get(2)); + assertEquals("Element Name,WhileCount,ConditionCount,RedundantCount,AssertCount,IfCount,ExceptionCount,ForeachCount,PrintCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,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,0,1,0,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,1,0,1,0,0,0,0,0,0,0,0,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,AssertCount,IfCount,ExceptionCount,ForeachCount,PrintCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,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,Resource Optimism,Duplicate Assert,Exception Catching Throwing,Print Statement,Lazy Test", String.join(",", headerEntries)/*, String.join(",",expectedEntries)*/); + assertEquals("Element Name,WhileCount,ConditionCount,RedundantCount,AssertCount,IfCount,ExceptionCount,ForeachCount,PrintCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,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)/*, String.join(",",expectedEntries)*/); } @Test void testContent_METHOD_Decrypt() throws IOException { - assertEquals(18, getMethodSmells().size()); + 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(16, contentEntries.stream().filter(e -> e.equals("false")).count()); // 18 methods smells - 2 detected smells + assertEquals(17, contentEntries.stream().filter(e -> e.equals("false")).count()); // 18 methods smells - 2 detected smells } @Test void testContent_METHOD_Encrypt() throws IOException { - assertEquals(18, getMethodSmells().size()); + 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(15, contentEntries.stream().filter(e -> e.equals("false")).count()); // 18 methods smells - 3 detected smells + assertEquals(16, contentEntries.stream().filter(e -> e.equals("false")).count()); // 18 methods smells - 3 detected smells } } From beeae6781d7c6f8a32fcc8e30a18fb579ae84b36 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Mon, 26 Oct 2020 23:57:40 -0300 Subject: [PATCH 38/42] Updated maven project version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b83a76d..7ac8ce6 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 edu.rit.se.testsmells TestSmellDetector - 0.2 + 0.3.1 jar edu.rit.se.testsmells.Main From 233f1c170a5c5101489e806926925cd89372bcdb Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Tue, 27 Oct 2020 04:52:45 -0300 Subject: [PATCH 39/42] Added Anonymous class handling at AbstractSmell.getFullMethodName --- .../se/testsmells/testsmell/AbstractSmell.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java index 8879037..3928fbe 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/AbstractSmell.java @@ -6,13 +6,13 @@ 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.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; public abstract class AbstractSmell { @@ -29,8 +29,18 @@ public AbstractSmell() { public abstract AbstractSmell recreate(); protected String getFullMethodName(CompilationUnit unit, T node) { - - String className = node.getParentNode().map(x -> (ClassOrInterfaceDeclaration) x).map(NodeWithSimpleName::getNameAsString).orElse(""); + 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(); } From 397e84022295f65f748c0d85e4b55540ca07b5e5 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Tue, 27 Oct 2020 04:59:05 -0300 Subject: [PATCH 40/42] Maven patch version increased --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7ac8ce6..c03dc09 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 edu.rit.se.testsmells TestSmellDetector - 0.3.1 + 0.3.2 jar edu.rit.se.testsmells.Main From 264f657958667357bd4607069219f9aa800e4509 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Fri, 25 Jun 2021 15:34:39 -0300 Subject: [PATCH 41/42] Renamed AssertCount to BadAssertCount and added TotalAssertCount --- .../se/testsmells/testsmell/smell/AssertionRoulette.java | 3 ++- .../testsmells/testsmell/MultiFilesIntegrationTest.java | 5 +++-- .../testsmell/ReportControllerMethodIntegrationTest.java | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java b/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java index a476f15..9524bf8 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/smell/AssertionRoulette.java @@ -76,7 +76,8 @@ 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)); addSmellyElement(testMethod); diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java index f4b36bf..513d46a 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/MultiFilesIntegrationTest.java @@ -12,7 +12,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; @IntegrationTest public class MultiFilesIntegrationTest { @@ -151,7 +152,7 @@ public void testBothClassAndMethodOrientedSmells_csvColumns() throws IOException assertEquals(3, outputFiles.size(), "Not generated all three reports"); - String expectedMethodHeader = "Element Name,WhileCount,ConditionCount,RedundantCount,AssertCount,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 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"; diff --git a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java index 73d015f..9f1850c 100644 --- a/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java +++ b/src/test/java/edu/rit/se/testsmells/testsmell/ReportControllerMethodIntegrationTest.java @@ -68,16 +68,16 @@ void testReport_METHOD() throws IOException { List entries = new BufferedReader(new FileReader(outputFile)).lines().collect(Collectors.toList()); - assertEquals("Element Name,WhileCount,ConditionCount,RedundantCount,AssertCount,IfCount,ExceptionCount,ForeachCount,PrintCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,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,0,1,0,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,1,0,1,0,0,0,0,0,0,0,0,0,0,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,true", entries.get(2)); + 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,AssertCount,IfCount,ExceptionCount,ForeachCount,PrintCount,SwitchCount,MysteryCount,ForCount,VerboseCount,ResourceOptimismCount,ThreadSleepCount,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)/*, String.join(",",expectedEntries)*/); + 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 From d30bc861b83f059f0a0f392c9700d56646810a91 Mon Sep 17 00:00:00 2001 From: Victor Guerra Veloso Date: Fri, 25 Jun 2021 15:50:22 -0300 Subject: [PATCH 42/42] Added final modifier to TestSmellDetector class - Addressing @ellieMayVelasquez comment on my PR; - Fixing getResource bad practice. --- .../java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java index d4baa27..2df0517 100644 --- a/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java +++ b/src/main/java/edu/rit/se/testsmells/testsmell/TestSmellDetector.java @@ -13,7 +13,7 @@ import java.util.Objects; import java.util.stream.Collectors; -public class TestSmellDetector { +public final class TestSmellDetector { private List inputStreams; private List testSmells;