diff --git a/.gitignore b/.gitignore index d74bfd55..eed8870d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,13 +3,6 @@ node_modules .clinic .DS_Store -dist/* -!dist/parser/ -dist/parser/* -!dist/parser/__snapshots__/ - -!dist/commands -dist/commands/* -!dist/commands/__snapshots__/ +dist ./resources/output.json diff --git a/dist/commands/__snapshots__/outputMetrics.test.js.snap b/dist/commands/__snapshots__/outputMetrics.test.js.snap deleted file mode 100644 index a0d0e148..00000000 --- a/dist/commands/__snapshots__/outputMetrics.test.js.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`outputMetrics writes json into file when metrics are present 1`] = `"{\\"nodes\\":[{\\"name\\":\\"/file/path1.test\\",\\"type\\":\\"File\\",\\"metrics\\":{\\"metric1\\":42,\\"metric2\\":43}},{\\"name\\":\\"/file/path2.test\\",\\"type\\":\\"File\\",\\"metrics\\":{\\"metric1\\":44,\\"metric2\\":45,\\"outgoing_dependencies\\":3,\\"incoming_dependencies\\":2,\\"instability\\":0.6}}],\\"info\\":[{\\"name\\":\\"/file/path3.unknown\\",\\"type\\":\\"File\\",\\"message\\":\\"Unknown language or file extension\\"},{\\"name\\":\\"/file/noExtension\\",\\"type\\":\\"File\\",\\"message\\":\\"Unknown language or file extension\\"}],\\"relationships\\":[{\\"from\\":\\"/file/path2.test\\",\\"to\\":\\"/file/path1.test\\",\\"metrics\\":{\\"coupling\\":100}}]}"`; diff --git a/jest.config.js b/jest.config.js index 62f7f80d..8863fc66 100644 --- a/jest.config.js +++ b/jest.config.js @@ -4,4 +4,5 @@ module.exports = { coverageDirectory: "/dist/coverage", collectCoverageFrom: ["/src/**/*.{ts,tsx}", "!**/node_modules/**"], coveragePathIgnorePatterns: ["(/__tests__/.*|\\.(test|e2e))\\.(ts|tsx)$"], + testPathIgnorePatterns: ["/dist/", "/node_modules/"], }; diff --git a/package.json b/package.json index 145908b4..10669547 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ }, "scripts": { "start": "tsc && node dist/app.js", - "test": "tsc && jest", + "test": "tsc && jest --maxWorkers=24", "format": "prettier --write \"./**/*\"", "format:quick": "pretty-quick --staged", "lint": "eslint \"src/**/*.ts\"", diff --git a/resources/java/Empty.java b/resources/java/Empty.java new file mode 100644 index 00000000..e69de29b diff --git a/resources/java/classes/ClassWithFieldsAndMethods.java b/resources/java/classes/ClassWithFieldsAndMethods.java new file mode 100644 index 00000000..504b0d7b --- /dev/null +++ b/resources/java/classes/ClassWithFieldsAndMethods.java @@ -0,0 +1,24 @@ +public class MyClass { + private int myNumber; // Variable + InnerClass innerClass; + + // Constructor + public MyClass(int initialNumber) { + this.myNumber = initialNumber; + this.innerClass = new InnerClass(); + innerClass.key = initialNumber; + } + + // Method + public void printNumber() { + System.out.println("My number is: " + myNumber); + } + + // Getter for the variable + public int getMyNumber() { + return myNumber; + } + public class InnerClass { + int key; + } +} \ No newline at end of file diff --git a/resources/java/classes/Classes.java b/resources/java/classes/Classes.java new file mode 100644 index 00000000..bc10179c --- /dev/null +++ b/resources/java/classes/Classes.java @@ -0,0 +1,6 @@ +class Student { //the student +} +public class Professor { //the professor + +} + diff --git a/resources/java/classes/Classlike.java b/resources/java/classes/Classlike.java new file mode 100644 index 00000000..f7207d6c --- /dev/null +++ b/resources/java/classes/Classlike.java @@ -0,0 +1,11 @@ +enum Food{ + Fish, + Vegetable +} +class Kitchen{ +} +interface World { +} +abstract class Abstraction{ + +} diff --git a/resources/java/classes/Comment.java b/resources/java/classes/Comment.java new file mode 100644 index 00000000..15be7f55 --- /dev/null +++ b/resources/java/classes/Comment.java @@ -0,0 +1,28 @@ + +//public class HelloWorld {} +/** + * public class MyClass { + * private int myNumber; // Variable + * InnerClass innerClass; + * + * // Constructor + * public MyClass(int initialNumber) { + * this.myNumber = initialNumber; + * this.innerClass = new InnerClass(); + * innerClass.key = initialNumber; + * } + * + * // Method + * public void printNumber() { + * System.out.println("My number is: " + myNumber); + * } + * + * // Getter for the variable + * public int getMyNumber() { + * return myNumber; + * } + * public class InnerClass { + * int key; + * } + * } + */ \ No newline at end of file diff --git a/resources/java/classes/NestedClasses.java b/resources/java/classes/NestedClasses.java new file mode 100644 index 00000000..27117d17 --- /dev/null +++ b/resources/java/classes/NestedClasses.java @@ -0,0 +1,17 @@ +public class NestedClassesExample { + public static class StaticNestedClass { + public void staticNestedMethod() { + System.out.println("Inside StaticNestedClass's method"); + } + } + + private class InnerClass { + public void innerMethod() { + System.out.println("Inside InnerClass's method"); + } + } + protected class Treasure{ + + } +} + diff --git a/resources/java/comment_lines/DifferentKindOfComments.java b/resources/java/comment_lines/DifferentKindOfComments.java new file mode 100644 index 00000000..59e88f33 --- /dev/null +++ b/resources/java/comment_lines/DifferentKindOfComments.java @@ -0,0 +1,37 @@ +// Single-line comment + +/* + * Multi-line comment + * + * This is another line of the multi-line comment. + */ + +/* + Multi-line comment containing line break + + + + This is another line of the multi-line comment. + */ +public class CommentExample { + /** + * Main method to demonstrate comments. + * @param number . + */ + public void doSomething(int number){ + number++; //increment + number--; /* short comment*/ + System.out.print(/*between the line*/ "hello world"); /*hello*/ //greeting + System.out.print(/* multiline + more multiline + */"hello"); + System.out.print( + //hello + "hello" + //hello + ); +// ╔════════════╗ +// ║ Hello! ║ +// ╚════════════╝ + } +} \ No newline at end of file diff --git a/resources/java/functions/AbstractClassFunction.java b/resources/java/functions/AbstractClassFunction.java new file mode 100644 index 00000000..5e477223 --- /dev/null +++ b/resources/java/functions/AbstractClassFunction.java @@ -0,0 +1,10 @@ +abstract class MyAbstractClass { + + // Abstract method in the abstract class + public abstract void abstractMethod(); //abstract + + // Concrete method in the abstract class + public void concreteMethod() { //concrete + System.out.println("Concrete method in MyAbstractClass"); + } +} \ No newline at end of file diff --git a/resources/java/functions/Constructor.java b/resources/java/functions/Constructor.java new file mode 100644 index 00000000..4caab6aa --- /dev/null +++ b/resources/java/functions/Constructor.java @@ -0,0 +1,80 @@ +public class Book { + private String title; + private String author; + private int pageCount; + + // Constructor with no parameters + public Book() { + this.title = "Unknown Title"; + this.author = "Unknown Author"; + this.pageCount = 0; + } + + // Constructor with title and author parameters + public Book(String title, String author) { + this.title = title; + this.author = author; + this.pageCount = 0; // Default page count + } + + // Constructor with all parameters + public Book(String title, String author, int pageCount) { + this.title = title; + this.author = author; + this.pageCount = pageCount; + } + + // Getter methods for accessing private fields + public String getTitle() { + return title; + } + + public String getAuthor() { + return author; + } + + public int getPageCount() { + return pageCount; + } + + // Setter methods for modifying private fields + public void setTitle(String title) { + this.title = title; + } + + public void setAuthor(String author) { + this.author = author; + } + + public void setPageCount(int pageCount) { + this.pageCount = pageCount; + } + + // Method to display book information + public void displayBookInfo() { + System.out.println("Title: " + title); + System.out.println("Author: " + author); + System.out.println("Page Count: " + pageCount); + } + + /** + * Main method to demonstrate the usage of the Book class with different constructors. + * + * @param args Command-line arguments (not used in this example). + */ + public static void main(String[] args) { + // Using different constructors to create Book objects + Book book1 = new Book(); // Using the default constructor + Book book2 = new Book("Java Programming", "John Doe"); // Using the constructor with title and author + Book book3 = new Book("Design Patterns", "Jane Smith", 300); // Using the constructor with all parameters + + // Displaying book information + book1.displayBookInfo(); + System.out.println(); + + book2.displayBookInfo(); + System.out.println(); + + book3.displayBookInfo(); + } +} \ No newline at end of file diff --git a/resources/java/functions/FunctionAccessModifier.java b/resources/java/functions/FunctionAccessModifier.java new file mode 100644 index 00000000..9ecffcd5 --- /dev/null +++ b/resources/java/functions/FunctionAccessModifier.java @@ -0,0 +1,19 @@ +public class AccessModifierExample { + + public void publicMethod() { + this.protectedMethod(); + System.out.println("This is a public method."); + } + + protected void protectedMethod() { + System.out.println("This is a protected method."); + } + + void defaultMethod() { + System.out.println("This is a default (package-private) method."); + } + + private void privateMethod() { + System.out.println("This is a private method."); + } +} \ No newline at end of file diff --git a/resources/java/functions/InterfaceFunction.java b/resources/java/functions/InterfaceFunction.java new file mode 100644 index 00000000..b18badf5 --- /dev/null +++ b/resources/java/functions/InterfaceFunction.java @@ -0,0 +1,8 @@ +public interface MyInterface { + // Abstract method in the interface + void interfaceMethod(); + + // Default method in the interface (introduced in Java 8) + default void defaultMethod() { + System.out.println("Default implementation in MyInterface"); + } \ No newline at end of file diff --git a/resources/java/functions/OverloadFuntion.java b/resources/java/functions/OverloadFuntion.java new file mode 100644 index 00000000..c8cccc3f --- /dev/null +++ b/resources/java/functions/OverloadFuntion.java @@ -0,0 +1,17 @@ +class OverloadedFunctionsExample { + + // Overloaded function with two integer parameters + public static int add(int a, int b) { + return a + b; + } + + // Overloaded function with three integer parameters + public static int add(int a, int b, int c) { + return a + b + c; + } + + // Overloaded function with two double parameters + public static double add(double a, double b) { + return a + b; + } +} \ No newline at end of file diff --git a/resources/java/functions/StaticFuntions.java b/resources/java/functions/StaticFuntions.java new file mode 100644 index 00000000..dfc97deb --- /dev/null +++ b/resources/java/functions/StaticFuntions.java @@ -0,0 +1,21 @@ +public class ExampleClass { + private static int staticVariable = 0; + private int instanceVariable = 0; + private String message = "message"; + public static void incrementStaticVariable() { + staticVariable++; + } + public static int returnStaticMessage() { + return "Hello"; + } + public void incrementInstanceVariable() { + instanceVariable++; + } + public void returnMessage() { + return this.message; + } + public static void main(String[] args) { + ExampleClass obj1 = new ExampleClass(5); + ExampleClass obj2 = new ExampleClass(10); + } +} \ No newline at end of file diff --git a/resources/java/lines_of_code/ClassForLOC.java b/resources/java/lines_of_code/ClassForLOC.java new file mode 100644 index 00000000..504b0d7b --- /dev/null +++ b/resources/java/lines_of_code/ClassForLOC.java @@ -0,0 +1,24 @@ +public class MyClass { + private int myNumber; // Variable + InnerClass innerClass; + + // Constructor + public MyClass(int initialNumber) { + this.myNumber = initialNumber; + this.innerClass = new InnerClass(); + innerClass.key = initialNumber; + } + + // Method + public void printNumber() { + System.out.println("My number is: " + myNumber); + } + + // Getter for the variable + public int getMyNumber() { + return myNumber; + } + public class InnerClass { + int key; + } +} \ No newline at end of file diff --git a/resources/java/lines_of_code/Linebreak.java b/resources/java/lines_of_code/Linebreak.java new file mode 100644 index 00000000..e253bbe2 --- /dev/null +++ b/resources/java/lines_of_code/Linebreak.java @@ -0,0 +1,6 @@ + +class Cat{ + + + +} diff --git a/resources/java/lines_of_code/MultilineLineOfCode.java b/resources/java/lines_of_code/MultilineLineOfCode.java new file mode 100644 index 00000000..434f7d38 --- /dev/null +++ b/resources/java/lines_of_code/MultilineLineOfCode.java @@ -0,0 +1,30 @@ +class MultiLine { + interface MyFunctionalInterface { + void myMethod(); + } + + + + String multiLineString = "This is a multi-line \n" + + "string using escape characters."; + + + String multiLineString = """ + This is a multi-line + string using text blocks. + """; + + int result = addNumbers( + 10, + 20, + 30 + ); + + + int[] numbers = { + 1, + 2, + 3, + 4 + }; +} \ No newline at end of file diff --git a/resources/java/mcc/IfStatements.java b/resources/java/mcc/IfStatements.java new file mode 100644 index 00000000..d229982c --- /dev/null +++ b/resources/java/mcc/IfStatements.java @@ -0,0 +1,26 @@ +public class IfStatementsExample { + + /** + * Example for multiple if statements. + * + * @param value The value to check. + */ + public void exampleIfStatement(int value) { + if (value > 0) return; + if (value > 0) { + System.out.println(value + " is greater than 0"); + } + if (value > 0 && value > 1 || value > 5) { + System.out.println(value + " is greater than 0"); + } else { + System.out.println(value + " is less than or equal to 0"); + } + if (value > 0) { + System.out.println(value + " is greater than 0"); + } else if (value < 0) { + System.out.println(value + " is less than 0"); + } else { + System.out.println(value + " is equal to 0"); + } + } +} \ No newline at end of file diff --git a/resources/java/mcc/SwitchStatement.java b/resources/java/mcc/SwitchStatement.java new file mode 100644 index 00000000..0a21c09e --- /dev/null +++ b/resources/java/mcc/SwitchStatement.java @@ -0,0 +1,46 @@ +public class SwitchStatementExample { + public static void main(String[] args) { + int dayOfWeek = 3; // Assuming 1 represents Sunday, 2 represents Monday, and so on... + + switch (dayOfWeek) { + case 1: + System.out.println("It's Sunday!"); + break; + case 2: + System.out.println("It's Monday!"); + break; + case 3: + System.out.println("It's Tuesday!"); + break; + case 4: + System.out.println("It's Wednesday!"); + break; + case 5: + System.out.println("It's Thursday!"); + break; + case 6: + System.out.println("It's Friday!"); + break; + case 7: + System.out.println("It's Saturday!"); + break; + default: + System.out.println("Invalid day of the week!"); + } + int number = 15; + + switch (true) { + case (number > 0 && number < 10): + System.out.println("The number is between 1 and 9"); + break; + case (number >= 10 || number < 20): + System.out.println("The number is between 10 and 19"); + break; + case (number >= 20): + System.out.println("The number is 20 or greater"); + break; + default: + System.out.println("The number is either negative or zero"); + } + } +} \ No newline at end of file diff --git a/resources/java/mcc/WhileAndForLoop.java b/resources/java/mcc/WhileAndForLoop.java new file mode 100644 index 00000000..f416349f --- /dev/null +++ b/resources/java/mcc/WhileAndForLoop.java @@ -0,0 +1,28 @@ +class Loop { + public void loopMethod(int n) { + for (int i = 0; i < n; i++) { + System.out.println("Iteration " + (i + 1)); + } + int[] numbers = {1, 2, 3, 4, 5}; + for (int num : numbers) { + System.out.println("Element: " + num); + } + int counter = 0; + while (counter < n) { + System.out.println("Iteration " + (counter + 1)); + counter++; + } + while (counter < n || counter == 3 && counter > 0) { + System.out.println("Iteration " + (counter + 1)); + counter++; + } + do { + System.out.print("Enter a number (0 to exit): "); + userInput = scanner.nextInt(); + + // Your code inside the loop goes here + System.out.println("You entered: " + userInput); + + } while (userInput == 0 || userInput > 0 && userInput == 2); + } +} diff --git a/resources/java/real_lines_of_code/InitializationBlock.java b/resources/java/real_lines_of_code/InitializationBlock.java new file mode 100644 index 00000000..954ea65c --- /dev/null +++ b/resources/java/real_lines_of_code/InitializationBlock.java @@ -0,0 +1,16 @@ +public class InitializationBlockExample { + + private String message; + + // initialization block + { + // Code inside this block is executed before any constructor + System.out.println("Executing instance initialization block"); + message = "Hello, World!"; + } + + // Constructor + public InitializationBlockExample() { + System.out.println("Executing constructor"); + } +} \ No newline at end of file diff --git a/resources/java/real_lines_of_code/InlineComment.java b/resources/java/real_lines_of_code/InlineComment.java new file mode 100644 index 00000000..f619a000 --- /dev/null +++ b/resources/java/real_lines_of_code/InlineComment.java @@ -0,0 +1,4 @@ +class Hello { + String greetingMessage; // the message to be printed + int i = 1: // i is initialized to 1 +} \ No newline at end of file diff --git a/resources/java/real_lines_of_code/MultilineRealLineOfCode.java b/resources/java/real_lines_of_code/MultilineRealLineOfCode.java new file mode 100644 index 00000000..657ee595 --- /dev/null +++ b/resources/java/real_lines_of_code/MultilineRealLineOfCode.java @@ -0,0 +1,68 @@ +class MultiLine { + interface MyFunctionalInterface { + void myMethod(); + } + String multiLineString = "This is a multi-line \n" + + "string using escape characters."; + + String multiLineString = """ + This is a multi-line + string using text blocks. + """; + int result = addNumbers( + 10, + 20, + 30 + ); + int[] numbers = { + 1, + 2, + 3, + 4 + }; + + private static int addNumbers(int a, int b, int c) { + return a + b + c; + } + private void doMultiLineLambdaExpression(){ + MyFunctionalInterface myFunction = () -> { + System.out.println("Statement 1"); + System.out.println("Statement 2"); + }; + } + private enum Book { + CRIMINAL, + NOVEL, + SCIENCE + } + +} +public class MethodChainingExample { + private String data; + // A method that returns the instance for chaining + public MethodChainingExample setData(String data) { + this.data = data; + return this; + } + + public MethodChainingExample processData() { + // Process data logic + System.out.println("Processing data: " + data); + return this; + } + + public MethodChainingExample displayResult() { + // Display result logic + System.out.println("Displaying result: " + data); + return this; + } + + public static void main(String[] args) { + MethodChainingExample example = new MethodChainingExample(); + + // Method chaining across multiple lines + example.setData("Hello") + .processData() + .displayResult(); + } +} \ No newline at end of file diff --git a/resources/java/real_lines_of_code/RealLineOfCodeAndComments.java b/resources/java/real_lines_of_code/RealLineOfCodeAndComments.java new file mode 100644 index 00000000..8c5a8ef2 --- /dev/null +++ b/resources/java/real_lines_of_code/RealLineOfCodeAndComments.java @@ -0,0 +1,55 @@ +package com.example.myproject; +import java.util.*; +public class ExampleClass { + // Member variable + private String myString; + + /** + * Constructor for initializing the class with a string. + * + * @param initialString The initial value for the `myString` variable. + */ + public ExampleClass(String initialString) { + this.myString = initialString; + } + + /** + * Getter method to retrieve the value of the `myString` variable. + * + * @return The current value of the `myString` variable. + */ + public String getMyString() { + return myString; + } + + /** + * Setter method to update the value of the `myString` variable. + * + * @param newString The new value to set for the `myString` variable. + */ + public void setMyString(String newString) { + this.myString = newString; + } + + /** + * A method to display the current value of the `myString` variable. + */ + public void displayString() { + System.out.println("Current value of myString: " + myString); + } + + /** + * Main method to demonstrate the usage of the `ExampleClass`. + * + * @param args Command-line arguments (not used in this example). + */ + public static void main(String[] args) { + ExampleClass exampleObject = new ExampleClass("Hello, World!"); + exampleObject.displayString(); + + exampleObject.setMyString("Updated String"); + exampleObject.displayString(); + } + +} + diff --git a/src/parser/GenericParser.test.ts b/src/parser/GenericParser.test.ts deleted file mode 100644 index ae80b5bc..00000000 --- a/src/parser/GenericParser.test.ts +++ /dev/null @@ -1,707 +0,0 @@ -import { GenericParser } from "./GenericParser"; -import fs from "fs"; -import { Configuration } from "./Configuration"; -import { strcmp } from "./helper/Helper"; -import { CouplingResult } from "./metrics/Metric"; - -describe("GenericParser", () => { - const phpTestResourcesPath = "./resources/php/"; - const csharpTestResourcesPath = "./resources/c-sharp/"; - const tsTestResourcesPath = "./resources/typescript/"; - const goTestResourcesPath = "./resources/go/"; - const pythonTestResourcesPath = "./resources/python/"; - const unknownTestResourcesPath = "./resources/unknown/"; - - /** - * Gets a parser configuration for the test cases. - * @param sourcesPath Path to the source files. - * @param parseDependencies Whether to enable parsing dependencies. - * @param formatFilePaths Whether to format the output file paths to be independent - * of project location and platform. - * When this is enabled, do not forget to also format the file path when accessing metric results from the output. - * You should use {@link formatPrintPath} for this, e.g.: - *

-     * results.fileMetrics.get(formatPrintPath(inputPath, config))
-     * 
- */ - function getParserConfiguration( - sourcesPath: string, - parseDependencies = false, - formatFilePaths = false - ) { - return new Configuration( - sourcesPath, - "invalid/output/path", - parseDependencies, - "", - false, - formatFilePaths, // For project location-independent testing - formatFilePaths // For platform-independent testing - ); - } - - /** - * Sorts the contents of the specified {@link CouplingResult} in a deterministic way. - * This is necessary as there can be deviations concerning the order - * in which files are found on different platforms. - * @param couplingResult The CouplingResult whose contents should be sorted. - */ - function sortCouplingResults(couplingResult: CouplingResult) { - // Sort the metrics in ascending order of the file paths - couplingResult.metrics = new Map( - [...couplingResult.metrics.entries()].sort((a, b) => strcmp(a[0], b[0])) - ); - couplingResult.relationships.sort((a, b) => { - // Unique ID for relationships adapted from metrics/coupling/Coupling.ts getRelationships(...) - const uniqueIdA = a.toNamespace + a.fromNamespace; - const uniqueIdB = b.toNamespace + b.fromNamespace; - return strcmp(uniqueIdA, uniqueIdB); - }); - } - - describe("parses PHP McCabeComplexity metric", () => { - it("should count branching statements correctly", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "if-statements.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(8); - }); - - it("should count functions and methods correctly", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "functions-and-methods.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(7); - }); - - it("should not count multiple return statements within functions and methods like sonar", async () => { - const inputPath = fs.realpathSync( - phpTestResourcesPath + "multiple-return-statements.php" - ); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(3); - }); - - it("should not count any class declaration", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "classes.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(0); - }); - - it("should count case statements correctly", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "case-statements.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(3); - }); - - it("should count try-catch-finally properly", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "throw-try-catch-finally.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(2); - }); - - it("should count loops properly", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "loops.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(4); - }); - }); - - describe("parses PHP classes metric", () => { - it("should count class declarations", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "classes.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("classes")?.metricValue).toBe(3); - }); - }); - - describe("parses PHP functions metric", () => { - it("should count function declarations", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "functions-and-methods.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("functions")?.metricValue).toBe(7); - }); - }); - - describe("parses PHP lines of code metric", () => { - it("should count number of lines correctly for a non-empty file with empty last line", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "empty-last-line.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("lines_of_code")?.metricValue).toBe(66); - }); - - it("should count number of lines correctly for a non-empty file with non-empty last line", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "php-example-code.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("lines_of_code")?.metricValue).toBe(65); - }); - - it("should count number of lines correctly for an empty file", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "empty.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("lines_of_code")?.metricValue).toBe(1); - }); - - it("should count number of lines correctly for an file with one non-empty line", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "one-line.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("lines_of_code")?.metricValue).toBe(1); - }); - - it("should count number of lines correctly for an file with just a line break", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "line-break.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("lines_of_code")?.metricValue).toBe(2); - }); - }); - - describe("parses PHP real lines of code metric", () => { - it("should count correctly for a non-empty file, ignoring comments and empty lines", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "php-example-code.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("real_lines_of_code")?.metricValue).toBe( - 43 - ); - }); - - it("should count correctly for an empty file", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "empty.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("real_lines_of_code")?.metricValue).toBe( - 0 - ); - }); - - it("should count correctly if there is a comment in the same line as actual code", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "same-line-comment.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("real_lines_of_code")?.metricValue).toBe( - 11 - ); - }); - }); - - describe("parses PHP commentLines metric", () => { - it( - "should count number of comment lines correctly, including line with curly brackets and comment " + - "lines inside block comment", - async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "php-example-code.php"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("comment_lines")?.metricValue).toBe( - 12 - ); - } - ); - }); - - describe("parses TypeScript McCabeComplexity metric", () => { - it("should count if statements correctly", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "if-statements.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(8); - }); - - it("should count functions and methods correctly", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "functions-and-methods.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(9); - }); - - it("should not count multiple return statements within functions and methods correctly", async () => { - const inputPath = fs.realpathSync( - tsTestResourcesPath + "multiple-return-statements.ts" - ); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(3); - }); - - it("should not count any class declaration", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "classes.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(0); - }); - - it("should count case but no default statements correctly", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "case-statements.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(3); - }); - - it("should count try-catch-finally properly", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "throw-try-catch-finally.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(2); - }); - - it("should count loops properly", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "loops.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(3); - }); - }); - - describe("parses TypeScript classes metric", () => { - it("should count class declarations", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "classes.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("classes")?.metricValue).toBe(3); - }); - }); - - describe("parses TypeScript functions metric", () => { - it("should count functions and methods properly", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "functions-and-methods.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("functions")?.metricValue).toBe(9); - }); - }); - - describe("parses TypeScript commentLines metric", () => { - it("should count properly, also counting file header, class description and doc block tag comment lines", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "comments.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("comment_lines")?.metricValue).toBe(14); - }); - - it("should count properly, also in the presence of multiple block comments in the same line", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "same-line-comment.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("comment_lines")?.metricValue).toBe(4); - }); - }); - - describe("parses TypeScript lines of code metric", () => { - it("should count number of lines correctly for a non-empty file with empty last line", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "ts-example-code.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("lines_of_code")?.metricValue).toBe(416); - }); - it("should count number of lines correctly for a non-empty file with non-empty last line", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "non-empty-last-line.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("lines_of_code")?.metricValue).toBe(415); - }); - - it("should count number of lines correctly for an empty file", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "empty.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("lines_of_code")?.metricValue).toBe(1); - }); - - it("should count number of lines correctly for an file with one non-empty line", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "one-line.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("lines_of_code")?.metricValue).toBe(1); - }); - - it("should count number of lines correctly for an file with just a line break", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "line-break.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("lines_of_code")?.metricValue).toBe(2); - }); - }); - - describe("parses TypeScript real lines of code metric", () => { - it("should count correctly for a non-empty file, ignoring comments and empty lines", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "real-lines-of-code.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("real_lines_of_code")?.metricValue).toBe( - 7 - ); - }); - - it("should count correctly for an empty file", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "empty.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("real_lines_of_code")?.metricValue).toBe( - 0 - ); - }); - - it("should count correctly for a file with a single comment", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "single-comment.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("real_lines_of_code")?.metricValue).toBe( - 0 - ); - }); - - it("should count correctly if there is a comment in the same line as actual code", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "same-line-comment.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("real_lines_of_code")?.metricValue).toBe( - 3 - ); - }); - - it("should count weirdly formatted lines of code correctly", async () => { - const inputPath = fs.realpathSync(tsTestResourcesPath + "weird-lines.ts"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("real_lines_of_code")?.metricValue).toBe( - 32 - ); - }); - }); - - describe("parses GO McCabeComplexity metric", () => { - it("should count if statements correctly", async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "if-statements.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(7); - }); - - it("should count functions and methods correctly", async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "functions-and-methods.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(2); - }); - - it("should not count multiple return statements within functions and methods correctly", async () => { - const inputPath = fs.realpathSync( - goTestResourcesPath + "multiple-return-statements.go" - ); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(3); - }); - - it("should not count any class declaration", async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "classes.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(0); - }); - - it("should count case statements correctly", async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "case-statements.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(3); - }); - - it("should count try-catch-finally properly", async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "throw-try-catch-finally.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(0); - }); - - it("should count loops properly", async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "loops.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(4); - }); - }); - - describe("parses GO functions metric", () => { - it("should count functions and methods properly", async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "functions-and-methods.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("functions")?.metricValue).toBe(2); - }); - }); - - describe("parses GO commentLines metric", () => { - it( - "should count number of comment lines correctly, including line with curly brackets, inline comments" + - " and lines of the block comment", - async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "if-statements.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("comment_lines")?.metricValue).toBe( - 6 - ); - } - ); - - it("should count number of comment lines correctly, including multiple successive comments", async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "go-example-code.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("comment_lines")?.metricValue).toBe(9); - }); - }); - - describe("parses GO lines of code metric", () => { - it("should count number of lines correctly for a non-empty file with empty last line", async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "empty-last-line.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("lines_of_code")?.metricValue).toBe(54); - }); - - it("should count number of lines correctly for a non-empty file with non-empty last line", async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "go-example-code.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("lines_of_code")?.metricValue).toBe(53); - }); - - it("should count number of lines correctly for an empty file", async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "empty.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("lines_of_code")?.metricValue).toBe(1); - }); - - it("should count number of lines correctly for an file with one non-empty line", async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "one-line.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("lines_of_code")?.metricValue).toBe(1); - }); - - it("should count number of lines correctly for an file with just a line break", async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "line-break.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("lines_of_code")?.metricValue).toBe(2); - }); - }); - - describe("parses Go real lines of code metric", () => { - it("should count correctly for a non-empty file, ignoring comments, inline comments and empty lines", async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "go-example-code.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("real_lines_of_code")?.metricValue).toBe( - 32 - ); - }); - - it("should count correctly for an empty file", async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "empty.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("real_lines_of_code")?.metricValue).toBe( - 0 - ); - }); - - it("should count correctly if there is a comment that includes code", async () => { - const inputPath = fs.realpathSync(goTestResourcesPath + "if-statements.go"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("real_lines_of_code")?.metricValue).toBe( - 19 - ); - }); - }); - - describe("parses Python McCabeComplexity metric", () => { - it("should count if statements correctly", async () => { - const inputPath = fs.realpathSync(pythonTestResourcesPath + "if.py"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("mcc")?.metricValue).toBe(4); - }); - }); - - describe("parses Python comment lines metric", () => { - it.skip("should count correctly, excluding inline and block comments", async () => { - const inputPath = fs.realpathSync(pythonTestResourcesPath + "loops.py"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("comment_lines")?.metricValue).toBe(5); - }); - }); - - describe("parses Python real lines of code metric", () => { - it("should count correctly for a non-empty file with pythons non-C-syntax code blocks", async () => { - const inputPath = fs.realpathSync(pythonTestResourcesPath + "blocks.py"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("real_lines_of_code")?.metricValue).toBe( - 9 - ); - }); - - it("should count correctly for an empty file", async () => { - const inputPath = fs.realpathSync(pythonTestResourcesPath + "empty.py"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("real_lines_of_code")?.metricValue).toBe( - 0 - ); - }); - - it("should count correctly for a non-empty file with nested loops and comments", async () => { - const inputPath = fs.realpathSync(pythonTestResourcesPath + "loops.py"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("real_lines_of_code")?.metricValue).toBe( - 4 - ); - }); - - it.skip("should count correctly in the presence of block comments", async () => { - const inputPath = fs.realpathSync(pythonTestResourcesPath + "block-comment.py"); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - expect(results.fileMetrics.get(inputPath)?.get("real_lines_of_code")?.metricValue).toBe( - 3 - ); - }); - }); - - describe("parsing PHP dependencies", () => { - it("should calculate the right dependencies and coupling metrics", async () => { - const inputPath = fs.realpathSync(phpTestResourcesPath + "coupling-examples/"); - const parser = new GenericParser(getParserConfiguration(inputPath, true, true)); - - const results = await parser.calculateMetrics(); - const couplingResult = results.couplingMetrics; - sortCouplingResults(couplingResult); - - expect(couplingResult).toMatchSnapshot(); - }); - }); - - describe("parsing C# dependencies", () => { - it("should calculate the right dependencies and coupling metrics", async () => { - const inputPath = fs.realpathSync(csharpTestResourcesPath + "coupling-examples/"); - const parser = new GenericParser(getParserConfiguration(inputPath, true, true)); - - const results = await parser.calculateMetrics(); - const couplingResult = results.couplingMetrics; - sortCouplingResults(couplingResult); - - expect(couplingResult).toMatchSnapshot(); - }, 10000); - }); - - describe("Include files with unknown or no file extension", () => { - it("should list files with unknown file extension", async () => { - const inputPath = fs.realpathSync(unknownTestResourcesPath); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - const filePath = fs.realpathSync(unknownTestResourcesPath + "example.unknownExtension"); - expect(results.unknownFiles.includes(filePath)).toBe(true); - }); - - it("should list files with no file extension", async () => { - const inputPath = fs.realpathSync(unknownTestResourcesPath); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - const filePath = fs.realpathSync(unknownTestResourcesPath + "ExampleWithoutExtension"); - expect(results.unknownFiles.includes(filePath)).toBe(true); - }); - - it("should still list files with known extension", async () => { - const inputPath = fs.realpathSync(unknownTestResourcesPath); - const parser = new GenericParser(getParserConfiguration(inputPath)); - const results = await parser.calculateMetrics(); - - const filePath = fs.realpathSync(unknownTestResourcesPath + "known.java"); - expect(results.fileMetrics.has(filePath)).toBe(true); - }); - }); -}); diff --git a/src/parser/MetricCalculator.ts b/src/parser/MetricCalculator.ts index c1a3170f..678bb2da 100644 --- a/src/parser/MetricCalculator.ts +++ b/src/parser/MetricCalculator.ts @@ -56,7 +56,7 @@ export class MetricCalculator { dlog(" ------------ Parsing File " + parseFile.filePath + ": ------------ "); - const tree = TreeParser.getParseTree(parseFile); + const tree = await TreeParser.getParseTreeAsync(parseFile); for (const metric of this.fileMetrics) { resultPromises.push( diff --git a/src/parser/__snapshots__/GenericParser.test.ts.snap b/src/parser/__snapshots__/GenericParser.test.ts.snap deleted file mode 100644 index bf621ada..00000000 --- a/src/parser/__snapshots__/GenericParser.test.ts.snap +++ /dev/null @@ -1,361 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`GenericParser parsing C# dependencies should calculate the right dependencies and coupling metrics 1`] = ` -Object { - "metrics": Map { - "BlubController.cs" => Object { - "coupling_between_objects": 8, - "incoming_dependencies": 7, - "instability": 0.125, - "outgoing_dependencies": 1, - }, - "Library\\\\FunctionCalls.cs" => Object { - "coupling_between_objects": 6, - "incoming_dependencies": 2, - "instability": 0.6666666666666666, - "outgoing_dependencies": 4, - }, - "Library\\\\IAnotherParameterTypes.cs" => Object { - "coupling_between_objects": 1, - "incoming_dependencies": 1, - "instability": 0, - "outgoing_dependencies": 0, - }, - "Library\\\\IParameterTypes.cs" => Object { - "coupling_between_objects": 3, - "incoming_dependencies": 2, - "instability": 0.3333333333333333, - "outgoing_dependencies": 1, - }, - "Library\\\\MyCustomArgumentNullException.cs" => Object { - "coupling_between_objects": 1, - "incoming_dependencies": 1, - "instability": 0, - "outgoing_dependencies": 0, - }, - "Library\\\\ObjectCreations.cs" => Object { - "coupling_between_objects": 8, - "incoming_dependencies": 1, - "instability": 0.875, - "outgoing_dependencies": 7, - }, - "Library\\\\ParameterTypes.cs" => Object { - "coupling_between_objects": 5, - "incoming_dependencies": 3, - "instability": 0.4, - "outgoing_dependencies": 2, - }, - "Library\\\\ParameterTypesSpecialized.cs" => Object { - "coupling_between_objects": 2, - "incoming_dependencies": 0, - "instability": 1, - "outgoing_dependencies": 2, - }, - }, - "relationships": Array [ - Object { - "fromClassName": "ParameterTypes", - "fromNamespace": "App.CouplingExamples.Library.ParameterTypes", - "fromSource": "Library\\\\ParameterTypes.cs", - "toClassName": "FunctionCalls", - "toNamespace": "App.CouplingExamples.Library.FunctionCalls", - "toSource": "Library\\\\FunctionCalls.cs", - "usageType": "usage", - }, - Object { - "fromClassName": "BlubControllerOne1", - "fromNamespace": "App.CouplingExamplesOne.BlubControllerOne1", - "fromSource": "BlubController.cs", - "toClassName": "FunctionCalls", - "toNamespace": "App.CouplingExamples.Library.FunctionCalls", - "toSource": "Library\\\\FunctionCalls.cs", - "usageType": "usage", - }, - Object { - "fromClassName": "IParameterTypes", - "fromNamespace": "App.CouplingExamples.Library.IParameterTypes", - "fromSource": "Library\\\\IParameterTypes.cs", - "toClassName": "IAnotherParameterTypes", - "toNamespace": "App.CouplingExamples.Library.IAnotherParameterTypes", - "toSource": "Library\\\\IAnotherParameterTypes.cs", - "usageType": "implements", - }, - Object { - "fromClassName": "ObjectCreations", - "fromNamespace": "App.CouplingExamples.Library.ObjectCreations", - "fromSource": "Library\\\\ObjectCreations.cs", - "toClassName": "IParameterTypes", - "toNamespace": "App.CouplingExamples.Library.IParameterTypes", - "toSource": "Library\\\\IParameterTypes.cs", - "usageType": "usage", - }, - Object { - "fromClassName": "ParameterTypesSpecialized", - "fromNamespace": "App.CouplingExamples.Library.ParameterTypesSpecialized", - "fromSource": "Library\\\\ParameterTypesSpecialized.cs", - "toClassName": "IParameterTypes", - "toNamespace": "App.CouplingExamples.Library.IParameterTypes", - "toSource": "Library\\\\IParameterTypes.cs", - "usageType": "implements", - }, - Object { - "fromClassName": "ObjectCreations", - "fromNamespace": "App.CouplingExamples.Library.ObjectCreations", - "fromSource": "Library\\\\ObjectCreations.cs", - "toClassName": "MyCustomArgumentNullException", - "toNamespace": "App.CouplingExamples.Library.MyCustomArgumentNullException", - "toSource": "Library\\\\MyCustomArgumentNullException.cs", - "usageType": "usage", - }, - Object { - "fromClassName": "ParameterTypes", - "fromNamespace": "App.CouplingExamples.Library.ParameterTypes", - "fromSource": "Library\\\\ParameterTypes.cs", - "toClassName": "ObjectCreations", - "toNamespace": "App.CouplingExamples.Library.ObjectCreations", - "toSource": "Library\\\\ObjectCreations.cs", - "usageType": "usage", - }, - Object { - "fromClassName": "FunctionCalls", - "fromNamespace": "App.CouplingExamples.Library.FunctionCalls", - "fromSource": "Library\\\\FunctionCalls.cs", - "toClassName": "ParameterTypes", - "toNamespace": "App.CouplingExamples.Library.ParameterTypes", - "toSource": "Library\\\\ParameterTypes.cs", - "usageType": "usage", - }, - Object { - "fromClassName": "ObjectCreations", - "fromNamespace": "App.CouplingExamples.Library.ObjectCreations", - "fromSource": "Library\\\\ObjectCreations.cs", - "toClassName": "ParameterTypes", - "toNamespace": "App.CouplingExamples.Library.ParameterTypes", - "toSource": "Library\\\\ParameterTypes.cs", - "usageType": "usage", - }, - Object { - "fromClassName": "ParameterTypesSpecialized", - "fromNamespace": "App.CouplingExamples.Library.ParameterTypesSpecialized", - "fromSource": "Library\\\\ParameterTypesSpecialized.cs", - "toClassName": "ParameterTypes", - "toNamespace": "App.CouplingExamples.Library.ParameterTypes", - "toSource": "Library\\\\ParameterTypes.cs", - "usageType": "extends", - }, - Object { - "fromClassName": "FunctionCalls", - "fromNamespace": "App.CouplingExamples.Library.FunctionCalls", - "fromSource": "Library\\\\FunctionCalls.cs", - "toClassName": "BlubControllerOne1", - "toNamespace": "App.CouplingExamplesOne.BlubControllerOne1", - "toSource": "BlubController.cs", - "usageType": "usage", - }, - Object { - "fromClassName": "ObjectCreations", - "fromNamespace": "App.CouplingExamples.Library.ObjectCreations", - "fromSource": "Library\\\\ObjectCreations.cs", - "toClassName": "BlubControllerOne1", - "toNamespace": "App.CouplingExamplesOne.BlubControllerOne1", - "toSource": "BlubController.cs", - "usageType": "usage", - }, - Object { - "fromClassName": "FunctionCalls", - "fromNamespace": "App.CouplingExamples.Library.FunctionCalls", - "fromSource": "Library\\\\FunctionCalls.cs", - "toClassName": "BlubControllerOne2", - "toNamespace": "App.CouplingExamplesOne.BlubControllerOne2", - "toSource": "BlubController.cs", - "usageType": "usage", - }, - Object { - "fromClassName": "ObjectCreations", - "fromNamespace": "App.CouplingExamples.Library.ObjectCreations", - "fromSource": "Library\\\\ObjectCreations.cs", - "toClassName": "BlubControllerOne2", - "toNamespace": "App.CouplingExamplesOne.BlubControllerOne2", - "toSource": "BlubController.cs", - "usageType": "usage", - }, - Object { - "fromClassName": "FunctionCalls", - "fromNamespace": "App.CouplingExamples.Library.FunctionCalls", - "fromSource": "Library\\\\FunctionCalls.cs", - "toClassName": "BlubControllerTwo1", - "toNamespace": "App.CouplingExamplesTwo.BlubControllerTwo1", - "toSource": "BlubController.cs", - "usageType": "usage", - }, - Object { - "fromClassName": "ObjectCreations", - "fromNamespace": "App.CouplingExamples.Library.ObjectCreations", - "fromSource": "Library\\\\ObjectCreations.cs", - "toClassName": "BlubControllerTwo1", - "toNamespace": "App.CouplingExamplesTwo.BlubControllerTwo1", - "toSource": "BlubController.cs", - "usageType": "usage", - }, - Object { - "fromClassName": "ObjectCreations", - "fromNamespace": "App.CouplingExamples.Library.ObjectCreations", - "fromSource": "Library\\\\ObjectCreations.cs", - "toClassName": "BlubControllerTwo2", - "toNamespace": "App.CouplingExamplesTwo.BlubControllerTwo2", - "toSource": "BlubController.cs", - "usageType": "usage", - }, - ], -} -`; - -exports[`GenericParser parsing PHP dependencies should calculate the right dependencies and coupling metrics 1`] = ` -Object { - "metrics": Map { - "AnotherController.php" => Object { - "coupling_between_objects": 4, - "incoming_dependencies": 0, - "instability": 1, - "outgoing_dependencies": 4, - }, - "AnotherControllerInterface.php" => Object { - "coupling_between_objects": 1, - "incoming_dependencies": 1, - "instability": 0, - "outgoing_dependencies": 0, - }, - "BlubController.php" => Object { - "coupling_between_objects": 5, - "incoming_dependencies": 2, - "instability": 0.6, - "outgoing_dependencies": 3, - }, - "ControllerInterface.php" => Object { - "coupling_between_objects": 3, - "incoming_dependencies": 3, - "instability": 0, - "outgoing_dependencies": 0, - }, - "FastControllerInterface.php" => Object { - "coupling_between_objects": 3, - "incoming_dependencies": 1, - "instability": 0.6666666666666666, - "outgoing_dependencies": 2, - }, - "Library\\\\Helper.php" => Object { - "coupling_between_objects": 4, - "incoming_dependencies": 3, - "instability": 0.25, - "outgoing_dependencies": 1, - }, - "Library\\\\HelperOutput.php" => Object { - "coupling_between_objects": 2, - "incoming_dependencies": 1, - "instability": 0.5, - "outgoing_dependencies": 1, - }, - }, - "relationships": Array [ - Object { - "fromClassName": "FastControllerInterface", - "fromNamespace": "App\\\\FastControllerInterface", - "fromSource": "FastControllerInterface.php", - "toClassName": "AnotherControllerInterface", - "toNamespace": "App\\\\AnotherControllerInterface", - "toSource": "AnotherControllerInterface.php", - "usageType": "implements", - }, - Object { - "fromClassName": "AnotherControllerOne", - "fromNamespace": "App\\\\CouplingExamplesOne\\\\AnotherControllerOne", - "fromSource": "AnotherController.php", - "toClassName": "ControllerInterface", - "toNamespace": "App\\\\ControllerInterface", - "toSource": "ControllerInterface.php", - "usageType": "implements", - }, - Object { - "fromClassName": "BlubControllerOne1", - "fromNamespace": "App\\\\CouplingExamplesOne\\\\BlubControllerOne1", - "fromSource": "BlubController.php", - "toClassName": "ControllerInterface", - "toNamespace": "App\\\\ControllerInterface", - "toSource": "ControllerInterface.php", - "usageType": "implements", - }, - Object { - "fromClassName": "FastControllerInterface", - "fromNamespace": "App\\\\FastControllerInterface", - "fromSource": "FastControllerInterface.php", - "toClassName": "ControllerInterface", - "toNamespace": "App\\\\ControllerInterface", - "toSource": "ControllerInterface.php", - "usageType": "implements", - }, - Object { - "fromClassName": "AnotherControllerOne", - "fromNamespace": "App\\\\CouplingExamplesOne\\\\AnotherControllerOne", - "fromSource": "AnotherController.php", - "toClassName": "BlubControllerOne1", - "toNamespace": "App\\\\CouplingExamplesOne\\\\BlubControllerOne1", - "toSource": "BlubController.php", - "usageType": "extends", - }, - Object { - "fromClassName": "BlubControllerTwo1", - "fromNamespace": "App\\\\CouplingExamplesTwo\\\\BlubControllerTwo1", - "fromSource": "BlubController.php", - "toClassName": "BlubControllerOne1", - "toNamespace": "App\\\\CouplingExamplesOne\\\\BlubControllerOne1", - "toSource": "BlubController.php", - "usageType": "extends", - }, - Object { - "fromClassName": "AnotherControllerOne", - "fromNamespace": "App\\\\CouplingExamplesOne\\\\AnotherControllerOne", - "fromSource": "AnotherController.php", - "toClassName": "Helper", - "toNamespace": "App\\\\CouplingExamples\\\\Library\\\\Helper", - "toSource": "Library\\\\Helper.php", - "usageType": "usage", - }, - Object { - "fromClassName": "BlubControllerOne1", - "fromNamespace": "App\\\\CouplingExamplesOne\\\\BlubControllerOne1", - "fromSource": "BlubController.php", - "toClassName": "Helper", - "toNamespace": "App\\\\CouplingExamples\\\\Library\\\\Helper", - "toSource": "Library\\\\Helper.php", - "usageType": "usage", - }, - Object { - "fromClassName": "HelperOutput", - "fromNamespace": "App\\\\CouplingExamples\\\\Library\\\\HelperOutput", - "fromSource": "Library\\\\HelperOutput.php", - "toClassName": "Helper", - "toNamespace": "App\\\\CouplingExamples\\\\Library\\\\Helper", - "toSource": "Library\\\\Helper.php", - "usageType": "usage", - }, - Object { - "fromClassName": "Helper", - "fromNamespace": "App\\\\CouplingExamples\\\\Library\\\\Helper", - "fromSource": "Library\\\\Helper.php", - "toClassName": "HelperOutput", - "toNamespace": "App\\\\CouplingExamples\\\\Library\\\\HelperOutput", - "toSource": "Library\\\\HelperOutput.php", - "usageType": "usage", - }, - Object { - "fromClassName": "AnotherControllerOne", - "fromNamespace": "App\\\\CouplingExamplesOne\\\\AnotherControllerOne", - "fromSource": "AnotherController.php", - "toClassName": "FastControllerInterface", - "toNamespace": "App\\\\FastControllerInterface", - "toSource": "FastControllerInterface.php", - "usageType": "implements", - }, - ], -} -`; diff --git a/src/parser/helper/TreeParser.ts b/src/parser/helper/TreeParser.ts index 5447a1f8..39137d82 100644 --- a/src/parser/helper/TreeParser.ts +++ b/src/parser/helper/TreeParser.ts @@ -18,6 +18,15 @@ export class TreeParser { const sourceCode = fs.readFileSync(parseFile.filePath, { encoding: "utf8" }); const tree = parser.parse(sourceCode); + if (tree === undefined) { + throw new Error("Syntax tree for file " + parseFile.filePath + " is undefined!"); + } + if (tree.rootNode === undefined) { + throw new Error( + "Root node of syntax tree for file " + parseFile.filePath + " is undefined!" + ); + } + TreeParser.cache.set(parseFile.filePath, tree); if (tree === undefined || tree.rootNode === undefined) { @@ -38,15 +47,19 @@ export class TreeParser { parser.setLanguage(languageToGrammar.get(parseFile.language)); const sourceCode = await fs.promises.readFile(parseFile.filePath, { encoding: "utf8" }); - const tree = parser.parse(sourceCode); - TreeParser.cache.set(parseFile.filePath, tree); - if (tree === undefined || tree.rootNode === undefined) { - console.error("Error: syntax tree for file " + parseFile.filePath + " is empty!"); - throw new Error("Error: syntax tree for file " + parseFile.filePath + " is empty!"); + if (tree === undefined) { + throw new Error("Syntax tree for file " + parseFile.filePath + " is undefined!"); + } + if (tree.rootNode === undefined) { + throw new Error( + "Root node of syntax tree for file " + parseFile.filePath + " is undefined!" + ); } + TreeParser.cache.set(parseFile.filePath, tree); + return tree; } } diff --git a/src/parser/metrics/Classes.ts b/src/parser/metrics/Classes.ts index df5a6a13..5d89c55a 100644 --- a/src/parser/metrics/Classes.ts +++ b/src/parser/metrics/Classes.ts @@ -1,7 +1,7 @@ import { QueryBuilder } from "../queries/QueryBuilder"; import { ExpressionMetricMapping, QueryStatementInterface } from "../helper/Model"; import { getQueryStatements } from "../helper/Helper"; -import { Metric, MetricResult, ParseFile } from "./Metric"; +import { FileMetric, Metric, MetricResult, ParseFile } from "./Metric"; import { debuglog, DebugLoggerFunction } from "node:util"; import { QueryMatch } from "tree-sitter"; import Parser from "tree-sitter"; @@ -36,6 +36,6 @@ export class Classes implements Metric { } getName(): string { - return "classes"; + return FileMetric.classes; } } diff --git a/src/parser/metrics/CommentLines.ts b/src/parser/metrics/CommentLines.ts index 027d44cd..7806bda4 100644 --- a/src/parser/metrics/CommentLines.ts +++ b/src/parser/metrics/CommentLines.ts @@ -1,7 +1,7 @@ import { QueryBuilder } from "../queries/QueryBuilder"; import { ExpressionMetricMapping, QueryStatementInterface } from "../helper/Model"; import { getQueryStatements } from "../helper/Helper"; -import { Metric, MetricResult, ParseFile } from "./Metric"; +import { FileMetric, Metric, MetricResult, ParseFile } from "./Metric"; import Parser, { QueryMatch, SyntaxNode } from "tree-sitter"; import { debuglog, DebugLoggerFunction } from "node:util"; @@ -57,6 +57,6 @@ export class CommentLines implements Metric { } getName(): string { - return "comment_lines"; + return FileMetric.commentLines; } } diff --git a/src/parser/metrics/Functions.ts b/src/parser/metrics/Functions.ts index a749549b..88162a42 100644 --- a/src/parser/metrics/Functions.ts +++ b/src/parser/metrics/Functions.ts @@ -1,7 +1,7 @@ import { QueryBuilder } from "../queries/QueryBuilder"; import { ExpressionMetricMapping, QueryStatementInterface } from "../helper/Model"; import { getQueryStatements } from "../helper/Helper"; -import { Metric, MetricResult, ParseFile } from "./Metric"; +import { FileMetric, Metric, MetricResult, ParseFile } from "./Metric"; import { debuglog, DebugLoggerFunction } from "node:util"; import { QueryMatch } from "tree-sitter"; import Parser from "tree-sitter"; @@ -36,6 +36,6 @@ export class Functions implements Metric { } getName(): string { - return "functions"; + return FileMetric.functions; } } diff --git a/src/parser/metrics/LinesOfCode.ts b/src/parser/metrics/LinesOfCode.ts index a9160455..58c5c953 100644 --- a/src/parser/metrics/LinesOfCode.ts +++ b/src/parser/metrics/LinesOfCode.ts @@ -1,4 +1,4 @@ -import { Metric, MetricResult, ParseFile } from "./Metric"; +import { FileMetric, Metric, MetricResult, ParseFile } from "./Metric"; import { debuglog, DebugLoggerFunction } from "node:util"; import Parser from "tree-sitter"; @@ -25,6 +25,6 @@ export class LinesOfCode implements Metric { } getName(): string { - return "lines_of_code"; + return FileMetric.linesOfCode; } } diff --git a/src/parser/metrics/McCabeComplexity.ts b/src/parser/metrics/McCabeComplexity.ts index c6556b38..f9a42f6c 100644 --- a/src/parser/metrics/McCabeComplexity.ts +++ b/src/parser/metrics/McCabeComplexity.ts @@ -5,7 +5,7 @@ import { OperatorQueryStatement, QueryStatementInterface, } from "../helper/Model"; -import { Metric, MetricResult, ParseFile } from "./Metric"; +import { FileMetric, Metric, MetricResult, ParseFile } from "./Metric"; import { debuglog, DebugLoggerFunction } from "node:util"; import { QueryMatch } from "tree-sitter"; import Parser from "tree-sitter"; @@ -66,6 +66,6 @@ export class McCabeComplexity implements Metric { } getName(): string { - return "mcc"; + return FileMetric.mcCabeComplexity; } } diff --git a/src/parser/metrics/Metric.ts b/src/parser/metrics/Metric.ts index 80bd6923..1cc50ff6 100644 --- a/src/parser/metrics/Metric.ts +++ b/src/parser/metrics/Metric.ts @@ -1,6 +1,18 @@ import Parser from "tree-sitter"; import { Languages } from "../helper/Languages"; +/** + * Names of all available file metrics. + */ +export enum FileMetric { + classes = "classes", + commentLines = "comment_lines", + functions = "functions", + linesOfCode = "lines_of_code", + mcCabeComplexity = "mcc", + realLinesOfCode = "real_lines_of_code", +} + /** * Interface for carrying the result of a metric calculation. */ diff --git a/src/parser/metrics/RealLinesOfCode.ts b/src/parser/metrics/RealLinesOfCode.ts index e8978968..28f3c53e 100644 --- a/src/parser/metrics/RealLinesOfCode.ts +++ b/src/parser/metrics/RealLinesOfCode.ts @@ -1,5 +1,5 @@ import { ExpressionMetricMapping } from "../helper/Model"; -import { Metric, MetricResult, ParseFile } from "./Metric"; +import { FileMetric, Metric, MetricResult, ParseFile } from "./Metric"; import Parser, { TreeCursor } from "tree-sitter"; import { getExpressionsByCategory } from "../helper/Helper"; import { debuglog, DebugLoggerFunction } from "node:util"; @@ -82,6 +82,6 @@ export class RealLinesOfCode implements Metric { } getName(): string { - return "real_lines_of_code"; + return FileMetric.realLinesOfCode; } } diff --git a/src/commands/__snapshots__/outputMetrics.test.ts.snap b/test/commands/__snapshots__/outputMetrics.test.ts.snap similarity index 100% rename from src/commands/__snapshots__/outputMetrics.test.ts.snap rename to test/commands/__snapshots__/outputMetrics.test.ts.snap diff --git a/src/commands/outputMetrics.test.ts b/test/commands/outputMetrics.test.ts similarity index 94% rename from src/commands/outputMetrics.test.ts rename to test/commands/outputMetrics.test.ts index adae7a78..dd4e4632 100644 --- a/src/commands/outputMetrics.test.ts +++ b/test/commands/outputMetrics.test.ts @@ -1,6 +1,6 @@ -import { outputAsJson } from "./outputMetrics"; +import { outputAsJson } from "../../src/commands/outputMetrics"; import fs from "fs"; -import { Relationship, MetricResult, CouplingResult } from "../parser/metrics/Metric"; +import { Relationship, MetricResult, CouplingResult } from "../../src/parser/metrics/Metric"; describe("outputMetrics", () => { describe("writes json into file ", () => { diff --git a/test/parser/CSharpMetric.test.ts b/test/parser/CSharpMetric.test.ts new file mode 100644 index 00000000..48f1a018 --- /dev/null +++ b/test/parser/CSharpMetric.test.ts @@ -0,0 +1,14 @@ +import { getCouplingMetrics } from "./TestHelper"; + +describe("C# metric tests", () => { + const csharpTestResourcesPath = "./resources/c-sharp/"; + + describe("parsing C# dependencies", () => { + it("should calculate the right dependencies and coupling metrics", async () => { + const couplingResult = await getCouplingMetrics( + csharpTestResourcesPath + "coupling-examples/" + ); + expect(couplingResult).toMatchSnapshot(); + }, 10000); + }); +}); diff --git a/src/parser/Configuration.test.ts b/test/parser/Configuration.test.ts similarity index 93% rename from src/parser/Configuration.test.ts rename to test/parser/Configuration.test.ts index cada42f7..548df18b 100644 --- a/src/parser/Configuration.test.ts +++ b/test/parser/Configuration.test.ts @@ -1,4 +1,4 @@ -import { Configuration } from "./Configuration"; +import { Configuration } from "../../src/parser/Configuration"; describe("Configuration", () => { describe("initializes settings", () => { diff --git a/test/parser/GoMetrics.test.ts b/test/parser/GoMetrics.test.ts new file mode 100644 index 00000000..5765a52f --- /dev/null +++ b/test/parser/GoMetrics.test.ts @@ -0,0 +1,144 @@ +import { testFileMetrics } from "./TestHelper"; +import { FileMetric } from "../../src/parser/metrics/Metric"; + +describe("Go metric tests", () => { + const goTestResourcesPath = "./resources/go/"; + + describe("parses Go McCabeComplexity metric", () => { + it("should count if statements correctly", async () => { + await testFileMetrics( + goTestResourcesPath + "if-statements.go", + FileMetric.mcCabeComplexity, + 7 + ); + }); + + it("should count functions and methods correctly", async () => { + await testFileMetrics( + goTestResourcesPath + "functions-and-methods.go", + FileMetric.mcCabeComplexity, + 2 + ); + }); + + it("should not count multiple return statements within functions and methods correctly", async () => { + await testFileMetrics( + goTestResourcesPath + "multiple-return-statements.go", + FileMetric.mcCabeComplexity, + 3 + ); + }); + + it("should not count any class declaration", async () => { + await testFileMetrics( + goTestResourcesPath + "classes.go", + FileMetric.mcCabeComplexity, + 0 + ); + }); + + it("should count case statements correctly", async () => { + await testFileMetrics( + goTestResourcesPath + "case-statements.go", + FileMetric.mcCabeComplexity, + 3 + ); + }); + + it("should count try-catch-finally properly", async () => { + await testFileMetrics( + goTestResourcesPath + "throw-try-catch-finally.go", + FileMetric.mcCabeComplexity, + 0 + ); + }); + + it("should count loops properly", async () => { + await testFileMetrics(goTestResourcesPath + "loops.go", FileMetric.mcCabeComplexity, 4); + }); + }); + + describe("parses Go functions metric", () => { + it("should count functions and methods properly", async () => { + await testFileMetrics( + goTestResourcesPath + "functions-and-methods.go", + FileMetric.functions, + 2 + ); + }); + }); + + describe("parses Go commentLines metric", () => { + it( + "should count number of comment lines correctly, including line with curly brackets, inline comments" + + " and lines of the block comment", + async () => { + await testFileMetrics( + goTestResourcesPath + "if-statements.go", + FileMetric.commentLines, + 6 + ); + } + ); + + it("should count number of comment lines correctly, including multiple successive comments", async () => { + await testFileMetrics( + goTestResourcesPath + "go-example-code.go", + FileMetric.commentLines, + 9 + ); + }); + }); + + describe("parses Go lines of code metric", () => { + it("should count number of lines correctly for a non-empty file with empty last line", async () => { + await testFileMetrics( + goTestResourcesPath + "empty-last-line.go", + FileMetric.linesOfCode, + 54 + ); + }); + + it("should count number of lines correctly for a non-empty file with non-empty last line", async () => { + await testFileMetrics( + goTestResourcesPath + "go-example-code.go", + FileMetric.linesOfCode, + 53 + ); + }); + + it("should count number of lines correctly for an empty file", async () => { + await testFileMetrics(goTestResourcesPath + "empty.go", FileMetric.linesOfCode, 1); + }); + + it("should count number of lines correctly for an file with one non-empty line", async () => { + await testFileMetrics(goTestResourcesPath + "one-line.go", FileMetric.linesOfCode, 1); + }); + + it("should count number of lines correctly for an file with just a line break", async () => { + await testFileMetrics(goTestResourcesPath + "line-break.go", FileMetric.linesOfCode, 2); + }); + }); + + describe("parses Go real lines of code metric", () => { + it("should count correctly for a non-empty file, ignoring comments, inline comments and empty lines", async () => { + await testFileMetrics( + goTestResourcesPath + "go-example-code.go", + FileMetric.realLinesOfCode, + 32 + ); + }); + + it("should count correctly for an empty file", async () => { + await testFileMetrics(goTestResourcesPath + "empty.go", FileMetric.realLinesOfCode, 0); + }); + + it("should count correctly if there is a comment that includes code", async () => { + await testFileMetrics( + goTestResourcesPath + "if-statements.go", + FileMetric.realLinesOfCode, + 19 + ); + }); + }); +}); diff --git a/test/parser/JavaMetrics.test.ts b/test/parser/JavaMetrics.test.ts new file mode 100644 index 00000000..3dc043b9 --- /dev/null +++ b/test/parser/JavaMetrics.test.ts @@ -0,0 +1,224 @@ +import { testFileMetrics } from "./TestHelper"; +import { FileMetric } from "../../src/parser/metrics/Metric"; +const javaTestResourcesPath = "./resources/java/"; + +describe("Java metrics tests.", () => { + describe("parses classes metric", () => { + it("should count classes with different access-modifier correctly", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.classes + "/Classes.java", + FileMetric.classes, + 2 + ); + }); + + it("should count the interface, class, abstract class and enum correctly", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.classes + "/Classlike.java", + FileMetric.classes, + 4 + ); + }); + + it("should count nested classes correctly", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.classes + "/NestedClasses.java", + FileMetric.classes, + 4 + ); + }); + + it("should not count the fields or methods of a class correctly", async () => { + await testFileMetrics( + javaTestResourcesPath + + "/" + + FileMetric.classes + + "/ClassWithFieldsAndMethods.java", + FileMetric.classes, + 2 + ); + }); + + it("should count zero for an empty java file", async () => { + await testFileMetrics(javaTestResourcesPath + "Empty.java", FileMetric.classes, 0); + }); + + it("should count zero for a file that contains only comments", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.classes + "/Comment.java", + FileMetric.classes, + 0 + ); + }); + }); + + describe("parses lines of code metric", () => { + it("should count lines of code for a non-empty file correctly", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.linesOfCode + "/ClassForLOC.java", + FileMetric.linesOfCode, + 24 + ); + }); + + it("should count lines of code for an empty file correctly", async () => { + await testFileMetrics(javaTestResourcesPath + "Empty.java", FileMetric.linesOfCode, 1); + }); + + it("should count lines of code correctly for a non-empty file that starts and ends with a line break", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.linesOfCode + "/Linebreak.java", + FileMetric.linesOfCode, + 7 + ); + }); + + it("should count lines of code for multiline strings, function calls, etc. correctly", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.linesOfCode + "/MultilineLineOfCode.java", + FileMetric.linesOfCode, + 30 + ); + }); + }); + + describe("parses real lines of code metric", () => { + it("should not count for comments", async () => { + await testFileMetrics( + javaTestResourcesPath + + "/" + + FileMetric.realLinesOfCode + + "/RealLineOfCodeAndComments.java", + FileMetric.realLinesOfCode, + 23 + ); + }); + + it("should count correctly if there is a comment in the same line as actual code", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.realLinesOfCode + "/InlineComment.java", + FileMetric.realLinesOfCode, + 4 + ); + }); + + it("should count correctly if there is multi-line code", async () => { + await testFileMetrics( + javaTestResourcesPath + + "/" + + FileMetric.realLinesOfCode + + "/MultilineRealLineOfCode.java", + FileMetric.realLinesOfCode, + 57 + ); + }); + + it("should count the code lines in the initialization block", async () => { + await testFileMetrics( + javaTestResourcesPath + + "/" + + FileMetric.realLinesOfCode + + "/InitializationBlock.java", + FileMetric.realLinesOfCode, + 10 + ); + }); + + it("should count zero real lines of code for an empty file", async () => { + await testFileMetrics( + javaTestResourcesPath + "Empty.java", + FileMetric.realLinesOfCode, + 0 + ); + }); + }); + + describe("parses functions metric", () => { + it("should count static and non-static function declarations correctly", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.functions + "/StaticFuntions.java", + FileMetric.functions, + 5 + ); + }); + + it("should count function declaration with different access-modifiers correctly", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.functions + "/FunctionAccessModifier.java", + FileMetric.functions, + 4 + ); + }); + + it("should count constructors as function declaration", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.functions + "/Constructor.java", + FileMetric.functions, + 11 + ); + }); + + it("should count for function declarations in an interface", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.functions + "/InterfaceFunction.java", + FileMetric.functions, + 2 + ); + }); + + it("should count function declarations in an abstract class", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.functions + "/AbstractClassFunction.java", + FileMetric.functions, + 2 + ); + }); + + it("should count overloading functions correctly", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.functions + "/OverloadFuntion.java", + FileMetric.functions, + 3 + ); + }); + }); + + describe("parses McCabeComplexity metric", () => { + it("should count one method declaration and its contained if-statements and logical operations correctly", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.mcCabeComplexity + "/IfStatements.java", + FileMetric.mcCabeComplexity, + 8 + ); + }); + + it("should count one method declaration, the number of for- and while-statements and the containing logical operators correctly", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.mcCabeComplexity + "/WhileAndForLoop.java", + FileMetric.mcCabeComplexity, + 10 + ); + }); + + it.skip("should count one method declaration, the number of switch-case-statements and the contained logical operators correctly", async () => { + await testFileMetrics( + javaTestResourcesPath + "/" + FileMetric.mcCabeComplexity + "/SwitchStatement.java", + FileMetric.mcCabeComplexity, + 13 + ); + }); + }); + + describe("parses comment-lines metric", () => { + it("should count the lines that contain inline, multi-line and single-line comments.", async () => { + await testFileMetrics( + javaTestResourcesPath + + "/" + + FileMetric.commentLines + + "/DifferentKindOfComments.java", + FileMetric.commentLines, + 28 + ); + }); + }); +}); diff --git a/test/parser/PHPMetrics.test.ts b/test/parser/PHPMetrics.test.ts new file mode 100644 index 00000000..a728b90e --- /dev/null +++ b/test/parser/PHPMetrics.test.ts @@ -0,0 +1,164 @@ +import { getCouplingMetrics, testFileMetrics } from "./TestHelper"; +import { FileMetric } from "../../src/parser/metrics/Metric"; + +describe("PHP metrics tests", () => { + const phpTestResourcesPath = "./resources/php/"; + + describe("parses PHP McCabeComplexity metric", () => { + it("should count branching statements correctly", async () => { + await testFileMetrics( + phpTestResourcesPath + "if-statements.php", + FileMetric.mcCabeComplexity, + 8 + ); + }); + + it("should count functions and methods correctly", async () => { + await testFileMetrics( + phpTestResourcesPath + "functions-and-methods.php", + FileMetric.mcCabeComplexity, + 7 + ); + }); + + it("should not count multiple return statements within functions and methods like sonar", async () => { + await testFileMetrics( + phpTestResourcesPath + "multiple-return-statements.php", + FileMetric.mcCabeComplexity, + 3 + ); + }); + + it("should not count any class declaration", async () => { + await testFileMetrics( + phpTestResourcesPath + "classes.php", + FileMetric.mcCabeComplexity, + 0 + ); + }); + + it("should count case statements correctly", async () => { + await testFileMetrics( + phpTestResourcesPath + "case-statements.php", + FileMetric.mcCabeComplexity, + 3 + ); + }); + + it("should count try-catch-finally properly", async () => { + await testFileMetrics( + phpTestResourcesPath + "throw-try-catch-finally.php", + FileMetric.mcCabeComplexity, + 2 + ); + }); + + it("should count loops properly", async () => { + await testFileMetrics( + phpTestResourcesPath + "loops.php", + FileMetric.mcCabeComplexity, + 4 + ); + }); + }); + + describe("parses PHP classes metric", () => { + it("should count class declarations", async () => { + await testFileMetrics(phpTestResourcesPath + "classes.php", FileMetric.classes, 3); + }); + }); + + describe("parses PHP functions metric", () => { + it("should count function declarations", async () => { + await testFileMetrics( + phpTestResourcesPath + "functions-and-methods.php", + FileMetric.functions, + 7 + ); + }); + }); + + describe("parses PHP lines of code metric", () => { + it("should count number of lines correctly for a non-empty file with empty last line", async () => { + await testFileMetrics( + phpTestResourcesPath + "empty-last-line.php", + FileMetric.linesOfCode, + 66 + ); + }); + + it("should count number of lines correctly for a non-empty file with non-empty last line", async () => { + await testFileMetrics( + phpTestResourcesPath + "php-example-code.php", + FileMetric.linesOfCode, + 65 + ); + }); + + it("should count number of lines correctly for an empty file", async () => { + await testFileMetrics(phpTestResourcesPath + "empty.php", FileMetric.linesOfCode, 1); + }); + + it("should count number of lines correctly for an file with one non-empty line", async () => { + await testFileMetrics(phpTestResourcesPath + "one-line.php", FileMetric.linesOfCode, 1); + }); + + it("should count number of lines correctly for an file with just a line break", async () => { + await testFileMetrics( + phpTestResourcesPath + "line-break.php", + FileMetric.linesOfCode, + 2 + ); + }); + }); + + describe("parses PHP real lines of code metric", () => { + it("should count correctly for a non-empty file, ignoring comments and empty lines", async () => { + await testFileMetrics( + phpTestResourcesPath + "php-example-code.php", + FileMetric.realLinesOfCode, + 43 + ); + }); + + it("should count correctly for an empty file", async () => { + await testFileMetrics( + phpTestResourcesPath + "empty.php", + FileMetric.realLinesOfCode, + 0 + ); + }); + + it("should count correctly if there is a comment in the same line as actual code", async () => { + await testFileMetrics( + phpTestResourcesPath + "same-line-comment.php", + FileMetric.realLinesOfCode, + 11 + ); + }); + }); + + describe("parses PHP commentLines metric", () => { + it( + "should count number of comment lines correctly, including line with curly brackets and comment " + + "lines inside block comment", + async () => { + await testFileMetrics( + phpTestResourcesPath + "php-example-code.php", + FileMetric.commentLines, + 12 + ); + } + ); + }); + + describe("parsing PHP dependencies", () => { + it("should calculate the right dependencies and coupling metrics", async () => { + const couplingResult = await getCouplingMetrics( + phpTestResourcesPath + "coupling-examples/" + ); + + expect(couplingResult).toMatchSnapshot(); + }); + }); +}); diff --git a/test/parser/PythonMetrics.test.ts b/test/parser/PythonMetrics.test.ts new file mode 100644 index 00000000..a988b094 --- /dev/null +++ b/test/parser/PythonMetrics.test.ts @@ -0,0 +1,56 @@ +import { testFileMetrics } from "./TestHelper"; +import { FileMetric } from "../../src/parser/metrics/Metric"; + +describe("Python metrics test", () => { + const pythonTestResourcesPath = "./resources/python/"; + + describe("parses Python McCabeComplexity metric", () => { + it("should count if statements correctly", async () => { + await testFileMetrics( + pythonTestResourcesPath + "if.py", + FileMetric.mcCabeComplexity, + 4 + ); + }); + }); + + describe("parses Python comment lines metric", () => { + it.skip("should count correctly, excluding inline and block comments", async () => { + await testFileMetrics(pythonTestResourcesPath + "loops.py", FileMetric.commentLines, 5); + }); + }); + + describe("parses Python real lines of code metric", () => { + it("should count correctly for a non-empty file with pythons non-C-syntax code blocks", async () => { + await testFileMetrics( + pythonTestResourcesPath + "blocks.py", + FileMetric.realLinesOfCode, + 9 + ); + }); + + it("should count correctly for an empty file", async () => { + await testFileMetrics( + pythonTestResourcesPath + "empty.py", + FileMetric.realLinesOfCode, + 0 + ); + }); + + it("should count correctly for a non-empty file with nested loops and comments", async () => { + await testFileMetrics( + pythonTestResourcesPath + "loops.py", + FileMetric.realLinesOfCode, + 4 + ); + }); + + it.skip("should count correctly in the presence of block comments", async () => { + await testFileMetrics( + pythonTestResourcesPath + "block-comment.py", + FileMetric.realLinesOfCode, + 3 + ); + }); + }); +}); diff --git a/test/parser/TestHelper.ts b/test/parser/TestHelper.ts new file mode 100644 index 00000000..1bae1692 --- /dev/null +++ b/test/parser/TestHelper.ts @@ -0,0 +1,90 @@ +import fs from "fs"; +import { GenericParser } from "../../src/parser/GenericParser"; +import { Configuration } from "../../src/parser/Configuration"; +import { CouplingResult, FileMetric } from "../../src/parser/metrics/Metric"; +import { strcmp } from "../../src/parser/helper/Helper"; + +/** + * Gets a parser configuration for the test cases. + * @param sourcesPath Path to the source files. + * @param parseDependencies Whether to enable parsing dependencies. + * @param formatFilePaths Whether to format the output file paths to be independent + * of project location and platform. + * When this is enabled, do not forget to also format the file path when accessing metric results from the output. + * You should use {@link formatPrintPath} for this, e.g.: + *

+ * results.fileMetrics.get(formatPrintPath(inputPath, config))
+ * 
+ */ +export function getParserConfiguration( + sourcesPath: string, + parseDependencies = false, + formatFilePaths = false +) { + return new Configuration( + sourcesPath, + "invalid/output/path", + parseDependencies, + "", + false, + formatFilePaths, // For project location-independent testing + formatFilePaths // For platform-independent testing + ); +} +/** + * Sorts the contents of the specified {@link CouplingResult} in a deterministic way. + * This is necessary as there can be deviations concerning the order + * in which files are found on different platforms. + * @param couplingResult The CouplingResult whose contents should be sorted. + */ +export function sortCouplingResults(couplingResult: CouplingResult) { + // Sort the metrics in ascending order of the file paths + couplingResult.metrics = new Map( + [...couplingResult.metrics.entries()].sort((a, b) => strcmp(a[0], b[0])) + ); + couplingResult.relationships.sort((a, b) => { + // Unique ID for relationships adapted from metrics/coupling/Coupling.ts getRelationships(...) + const uniqueIdA = a.toNamespace + a.fromNamespace; + const uniqueIdB = b.toNamespace + b.fromNamespace; + return strcmp(uniqueIdA, uniqueIdB); + }); +} + +/** + * Tests if the file metric is calculated correctly. + * @param inputPath Path to test source files. + * @param metric Name of the metric. + * @param expected Expected test result. + * */ +export async function testFileMetrics(inputPath: string, metric: FileMetric, expected: number) { + const realInputPath = fs.realpathSync(inputPath); + const parser = new GenericParser(getParserConfiguration(realInputPath)); + const results = await parser.calculateMetrics(); + expect(results.fileMetrics.get(realInputPath)?.get(metric)?.metricValue).toBe(expected); +} + +/** + * Gets the metrics for the specified test source files. + * @param inputPath Path to the test source files. + * @return The calculated metrics. + */ +export async function getFileMetrics(inputPath: string) { + const realInputPath = fs.realpathSync(inputPath); + const parser = new GenericParser(getParserConfiguration(realInputPath)); + return await parser.calculateMetrics(); +} + +/** + * Gets the coupling metrics for the specified path. + * @param inputPath Path to the test source files. + */ +export async function getCouplingMetrics(inputPath: string) { + const realInputPath = fs.realpathSync(inputPath); + const parser = new GenericParser(getParserConfiguration(realInputPath, true, true)); + + const results = await parser.calculateMetrics(); + const couplingResult = results.couplingMetrics; + sortCouplingResults(couplingResult); + + return couplingResult; +} diff --git a/test/parser/TypeScriptMetrics.test.ts b/test/parser/TypeScriptMetrics.test.ts new file mode 100644 index 00000000..adb5d2f4 --- /dev/null +++ b/test/parser/TypeScriptMetrics.test.ts @@ -0,0 +1,158 @@ +import { testFileMetrics } from "./TestHelper"; +import { FileMetric } from "../../src/parser/metrics/Metric"; + +describe("TypeScript metrics tests", () => { + const tsTestResourcesPath = "./resources/typescript/"; + + describe("parses TypeScript McCabeComplexity metric", () => { + it("should count if statements correctly", async () => { + await testFileMetrics( + tsTestResourcesPath + "if-statements.ts", + FileMetric.mcCabeComplexity, + 8 + ); + }); + + it("should count functions and methods correctly", async () => { + await testFileMetrics( + tsTestResourcesPath + "functions-and-methods.ts", + FileMetric.mcCabeComplexity, + 9 + ); + }); + + it("should not count multiple return statements within functions and methods correctly", async () => { + await testFileMetrics( + tsTestResourcesPath + "multiple-return-statements.ts", + FileMetric.mcCabeComplexity, + 3 + ); + }); + + it("should not count any class declaration", async () => { + await testFileMetrics( + tsTestResourcesPath + "classes.ts", + FileMetric.mcCabeComplexity, + 0 + ); + }); + + it("should count case but no default statements correctly", async () => { + await testFileMetrics( + tsTestResourcesPath + "case-statements.ts", + FileMetric.mcCabeComplexity, + 3 + ); + }); + + it("should count try-catch-finally properly", async () => { + await testFileMetrics( + tsTestResourcesPath + "throw-try-catch-finally.ts", + FileMetric.mcCabeComplexity, + 2 + ); + }); + + it("should count loops properly", async () => { + await testFileMetrics(tsTestResourcesPath + "loops.ts", FileMetric.mcCabeComplexity, 3); + }); + }); + + describe("parses TypeScript classes metric", () => { + it("should count class declarations", async () => { + await testFileMetrics(tsTestResourcesPath + "classes.ts", FileMetric.classes, 3); + }); + }); + + describe("parses TypeScript functions metric", () => { + it("should count functions and methods properly", async () => { + await testFileMetrics( + tsTestResourcesPath + "functions-and-methods.ts", + FileMetric.functions, + 9 + ); + }); + }); + + describe("parses TypeScript commentLines metric", () => { + it("should count properly, also counting file header, class description and doc block tag comment lines", async () => { + await testFileMetrics(tsTestResourcesPath + "comments.ts", FileMetric.commentLines, 14); + }); + + it("should count properly, also in the presence of multiple block comments in the same line", async () => { + await testFileMetrics( + tsTestResourcesPath + "same-line-comment.ts", + FileMetric.commentLines, + 4 + ); + }); + }); + + describe("parses TypeScript lines of code metric", () => { + it("should count number of lines correctly for a non-empty file with empty last line", async () => { + await testFileMetrics( + tsTestResourcesPath + "ts-example-code.ts", + FileMetric.linesOfCode, + 416 + ); + }); + + it("should count number of lines correctly for a non-empty file with non-empty last line", async () => { + await testFileMetrics( + tsTestResourcesPath + "non-empty-last-line.ts", + FileMetric.linesOfCode, + 415 + ); + }); + + it("should count number of lines correctly for an empty file", async () => { + await testFileMetrics(tsTestResourcesPath + "empty.ts", FileMetric.linesOfCode, 1); + }); + + it("should count number of lines correctly for an file with one non-empty line", async () => { + await testFileMetrics(tsTestResourcesPath + "one-line.ts", FileMetric.linesOfCode, 1); + }); + + it("should count number of lines correctly for an file with just a line break", async () => { + await testFileMetrics(tsTestResourcesPath + "line-break.ts", FileMetric.linesOfCode, 2); + }); + }); + + describe("parses TypeScript real lines of code metric", () => { + it("should count correctly for a non-empty file, ignoring comments and empty lines", async () => { + await testFileMetrics( + tsTestResourcesPath + "real-lines-of-code.ts", + FileMetric.realLinesOfCode, + 7 + ); + }); + + it("should count correctly for an empty file", async () => { + await testFileMetrics(tsTestResourcesPath + "empty.ts", FileMetric.realLinesOfCode, 0); + }); + + it("should count correctly for a file with a single comment", async () => { + await testFileMetrics( + tsTestResourcesPath + "single-comment.ts", + FileMetric.realLinesOfCode, + 0 + ); + }); + + it("should count correctly if there is a comment in the same line as actual code", async () => { + await testFileMetrics( + tsTestResourcesPath + "same-line-comment.ts", + FileMetric.realLinesOfCode, + 3 + ); + }); + + it("should count weirdly formatted lines of code correctly", async () => { + await testFileMetrics( + tsTestResourcesPath + "weird-lines.ts", + FileMetric.realLinesOfCode, + 32 + ); + }); + }); +}); diff --git a/test/parser/UnknownFiles.test.ts b/test/parser/UnknownFiles.test.ts new file mode 100644 index 00000000..d70f9fa5 --- /dev/null +++ b/test/parser/UnknownFiles.test.ts @@ -0,0 +1,31 @@ +import { getFileMetrics, testFileMetrics } from "./TestHelper"; +import { FileMetric } from "../../src/parser/metrics/Metric"; +import fs from "fs"; +import { GenericParser } from "../../src/parser/GenericParser"; + +describe("Test for handling files with unknown or no file extension", () => { + const unknownTestResourcesPath = "./resources/unknown/"; + + describe("Include files with unknown or no file extension", () => { + it("should list files with unknown file extension", async () => { + const results = await getFileMetrics(unknownTestResourcesPath); + + const filePath = fs.realpathSync(unknownTestResourcesPath + "example.unknownExtension"); + expect(results.unknownFiles.includes(filePath)).toBe(true); + }); + + it("should list files with no file extension", async () => { + const results = await getFileMetrics(unknownTestResourcesPath); + + const filePath = fs.realpathSync(unknownTestResourcesPath + "ExampleWithoutExtension"); + expect(results.unknownFiles.includes(filePath)).toBe(true); + }); + + it("should still list files with known extension", async () => { + const results = await getFileMetrics(unknownTestResourcesPath); + + const filePath = fs.realpathSync(unknownTestResourcesPath + "known.java"); + expect(results.fileMetrics.has(filePath)).toBe(true); + }); + }); +}); diff --git a/dist/parser/__snapshots__/GenericParser.test.js.snap b/test/parser/__snapshots__/CSharpMetric.test.ts.snap similarity index 58% rename from dist/parser/__snapshots__/GenericParser.test.js.snap rename to test/parser/__snapshots__/CSharpMetric.test.ts.snap index bf621ada..e3fcadee 100644 --- a/dist/parser/__snapshots__/GenericParser.test.js.snap +++ b/test/parser/__snapshots__/CSharpMetric.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`GenericParser parsing C# dependencies should calculate the right dependencies and coupling metrics 1`] = ` +exports[`C# metric tests parsing C# dependencies should calculate the right dependencies and coupling metrics 1`] = ` Object { "metrics": Map { "BlubController.cs" => Object { @@ -209,153 +209,3 @@ Object { ], } `; - -exports[`GenericParser parsing PHP dependencies should calculate the right dependencies and coupling metrics 1`] = ` -Object { - "metrics": Map { - "AnotherController.php" => Object { - "coupling_between_objects": 4, - "incoming_dependencies": 0, - "instability": 1, - "outgoing_dependencies": 4, - }, - "AnotherControllerInterface.php" => Object { - "coupling_between_objects": 1, - "incoming_dependencies": 1, - "instability": 0, - "outgoing_dependencies": 0, - }, - "BlubController.php" => Object { - "coupling_between_objects": 5, - "incoming_dependencies": 2, - "instability": 0.6, - "outgoing_dependencies": 3, - }, - "ControllerInterface.php" => Object { - "coupling_between_objects": 3, - "incoming_dependencies": 3, - "instability": 0, - "outgoing_dependencies": 0, - }, - "FastControllerInterface.php" => Object { - "coupling_between_objects": 3, - "incoming_dependencies": 1, - "instability": 0.6666666666666666, - "outgoing_dependencies": 2, - }, - "Library\\\\Helper.php" => Object { - "coupling_between_objects": 4, - "incoming_dependencies": 3, - "instability": 0.25, - "outgoing_dependencies": 1, - }, - "Library\\\\HelperOutput.php" => Object { - "coupling_between_objects": 2, - "incoming_dependencies": 1, - "instability": 0.5, - "outgoing_dependencies": 1, - }, - }, - "relationships": Array [ - Object { - "fromClassName": "FastControllerInterface", - "fromNamespace": "App\\\\FastControllerInterface", - "fromSource": "FastControllerInterface.php", - "toClassName": "AnotherControllerInterface", - "toNamespace": "App\\\\AnotherControllerInterface", - "toSource": "AnotherControllerInterface.php", - "usageType": "implements", - }, - Object { - "fromClassName": "AnotherControllerOne", - "fromNamespace": "App\\\\CouplingExamplesOne\\\\AnotherControllerOne", - "fromSource": "AnotherController.php", - "toClassName": "ControllerInterface", - "toNamespace": "App\\\\ControllerInterface", - "toSource": "ControllerInterface.php", - "usageType": "implements", - }, - Object { - "fromClassName": "BlubControllerOne1", - "fromNamespace": "App\\\\CouplingExamplesOne\\\\BlubControllerOne1", - "fromSource": "BlubController.php", - "toClassName": "ControllerInterface", - "toNamespace": "App\\\\ControllerInterface", - "toSource": "ControllerInterface.php", - "usageType": "implements", - }, - Object { - "fromClassName": "FastControllerInterface", - "fromNamespace": "App\\\\FastControllerInterface", - "fromSource": "FastControllerInterface.php", - "toClassName": "ControllerInterface", - "toNamespace": "App\\\\ControllerInterface", - "toSource": "ControllerInterface.php", - "usageType": "implements", - }, - Object { - "fromClassName": "AnotherControllerOne", - "fromNamespace": "App\\\\CouplingExamplesOne\\\\AnotherControllerOne", - "fromSource": "AnotherController.php", - "toClassName": "BlubControllerOne1", - "toNamespace": "App\\\\CouplingExamplesOne\\\\BlubControllerOne1", - "toSource": "BlubController.php", - "usageType": "extends", - }, - Object { - "fromClassName": "BlubControllerTwo1", - "fromNamespace": "App\\\\CouplingExamplesTwo\\\\BlubControllerTwo1", - "fromSource": "BlubController.php", - "toClassName": "BlubControllerOne1", - "toNamespace": "App\\\\CouplingExamplesOne\\\\BlubControllerOne1", - "toSource": "BlubController.php", - "usageType": "extends", - }, - Object { - "fromClassName": "AnotherControllerOne", - "fromNamespace": "App\\\\CouplingExamplesOne\\\\AnotherControllerOne", - "fromSource": "AnotherController.php", - "toClassName": "Helper", - "toNamespace": "App\\\\CouplingExamples\\\\Library\\\\Helper", - "toSource": "Library\\\\Helper.php", - "usageType": "usage", - }, - Object { - "fromClassName": "BlubControllerOne1", - "fromNamespace": "App\\\\CouplingExamplesOne\\\\BlubControllerOne1", - "fromSource": "BlubController.php", - "toClassName": "Helper", - "toNamespace": "App\\\\CouplingExamples\\\\Library\\\\Helper", - "toSource": "Library\\\\Helper.php", - "usageType": "usage", - }, - Object { - "fromClassName": "HelperOutput", - "fromNamespace": "App\\\\CouplingExamples\\\\Library\\\\HelperOutput", - "fromSource": "Library\\\\HelperOutput.php", - "toClassName": "Helper", - "toNamespace": "App\\\\CouplingExamples\\\\Library\\\\Helper", - "toSource": "Library\\\\Helper.php", - "usageType": "usage", - }, - Object { - "fromClassName": "Helper", - "fromNamespace": "App\\\\CouplingExamples\\\\Library\\\\Helper", - "fromSource": "Library\\\\Helper.php", - "toClassName": "HelperOutput", - "toNamespace": "App\\\\CouplingExamples\\\\Library\\\\HelperOutput", - "toSource": "Library\\\\HelperOutput.php", - "usageType": "usage", - }, - Object { - "fromClassName": "AnotherControllerOne", - "fromNamespace": "App\\\\CouplingExamplesOne\\\\AnotherControllerOne", - "fromSource": "AnotherController.php", - "toClassName": "FastControllerInterface", - "toNamespace": "App\\\\FastControllerInterface", - "toSource": "FastControllerInterface.php", - "usageType": "implements", - }, - ], -} -`; diff --git a/test/parser/__snapshots__/PHPMetrics.test.ts.snap b/test/parser/__snapshots__/PHPMetrics.test.ts.snap new file mode 100644 index 00000000..c1591d59 --- /dev/null +++ b/test/parser/__snapshots__/PHPMetrics.test.ts.snap @@ -0,0 +1,151 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PHP metrics tests parsing PHP dependencies should calculate the right dependencies and coupling metrics 1`] = ` +Object { + "metrics": Map { + "AnotherController.php" => Object { + "coupling_between_objects": 4, + "incoming_dependencies": 0, + "instability": 1, + "outgoing_dependencies": 4, + }, + "AnotherControllerInterface.php" => Object { + "coupling_between_objects": 1, + "incoming_dependencies": 1, + "instability": 0, + "outgoing_dependencies": 0, + }, + "BlubController.php" => Object { + "coupling_between_objects": 5, + "incoming_dependencies": 2, + "instability": 0.6, + "outgoing_dependencies": 3, + }, + "ControllerInterface.php" => Object { + "coupling_between_objects": 3, + "incoming_dependencies": 3, + "instability": 0, + "outgoing_dependencies": 0, + }, + "FastControllerInterface.php" => Object { + "coupling_between_objects": 3, + "incoming_dependencies": 1, + "instability": 0.6666666666666666, + "outgoing_dependencies": 2, + }, + "Library\\\\Helper.php" => Object { + "coupling_between_objects": 4, + "incoming_dependencies": 3, + "instability": 0.25, + "outgoing_dependencies": 1, + }, + "Library\\\\HelperOutput.php" => Object { + "coupling_between_objects": 2, + "incoming_dependencies": 1, + "instability": 0.5, + "outgoing_dependencies": 1, + }, + }, + "relationships": Array [ + Object { + "fromClassName": "FastControllerInterface", + "fromNamespace": "App\\\\FastControllerInterface", + "fromSource": "FastControllerInterface.php", + "toClassName": "AnotherControllerInterface", + "toNamespace": "App\\\\AnotherControllerInterface", + "toSource": "AnotherControllerInterface.php", + "usageType": "implements", + }, + Object { + "fromClassName": "AnotherControllerOne", + "fromNamespace": "App\\\\CouplingExamplesOne\\\\AnotherControllerOne", + "fromSource": "AnotherController.php", + "toClassName": "ControllerInterface", + "toNamespace": "App\\\\ControllerInterface", + "toSource": "ControllerInterface.php", + "usageType": "implements", + }, + Object { + "fromClassName": "BlubControllerOne1", + "fromNamespace": "App\\\\CouplingExamplesOne\\\\BlubControllerOne1", + "fromSource": "BlubController.php", + "toClassName": "ControllerInterface", + "toNamespace": "App\\\\ControllerInterface", + "toSource": "ControllerInterface.php", + "usageType": "implements", + }, + Object { + "fromClassName": "FastControllerInterface", + "fromNamespace": "App\\\\FastControllerInterface", + "fromSource": "FastControllerInterface.php", + "toClassName": "ControllerInterface", + "toNamespace": "App\\\\ControllerInterface", + "toSource": "ControllerInterface.php", + "usageType": "implements", + }, + Object { + "fromClassName": "AnotherControllerOne", + "fromNamespace": "App\\\\CouplingExamplesOne\\\\AnotherControllerOne", + "fromSource": "AnotherController.php", + "toClassName": "BlubControllerOne1", + "toNamespace": "App\\\\CouplingExamplesOne\\\\BlubControllerOne1", + "toSource": "BlubController.php", + "usageType": "extends", + }, + Object { + "fromClassName": "BlubControllerTwo1", + "fromNamespace": "App\\\\CouplingExamplesTwo\\\\BlubControllerTwo1", + "fromSource": "BlubController.php", + "toClassName": "BlubControllerOne1", + "toNamespace": "App\\\\CouplingExamplesOne\\\\BlubControllerOne1", + "toSource": "BlubController.php", + "usageType": "extends", + }, + Object { + "fromClassName": "AnotherControllerOne", + "fromNamespace": "App\\\\CouplingExamplesOne\\\\AnotherControllerOne", + "fromSource": "AnotherController.php", + "toClassName": "Helper", + "toNamespace": "App\\\\CouplingExamples\\\\Library\\\\Helper", + "toSource": "Library\\\\Helper.php", + "usageType": "usage", + }, + Object { + "fromClassName": "BlubControllerOne1", + "fromNamespace": "App\\\\CouplingExamplesOne\\\\BlubControllerOne1", + "fromSource": "BlubController.php", + "toClassName": "Helper", + "toNamespace": "App\\\\CouplingExamples\\\\Library\\\\Helper", + "toSource": "Library\\\\Helper.php", + "usageType": "usage", + }, + Object { + "fromClassName": "HelperOutput", + "fromNamespace": "App\\\\CouplingExamples\\\\Library\\\\HelperOutput", + "fromSource": "Library\\\\HelperOutput.php", + "toClassName": "Helper", + "toNamespace": "App\\\\CouplingExamples\\\\Library\\\\Helper", + "toSource": "Library\\\\Helper.php", + "usageType": "usage", + }, + Object { + "fromClassName": "Helper", + "fromNamespace": "App\\\\CouplingExamples\\\\Library\\\\Helper", + "fromSource": "Library\\\\Helper.php", + "toClassName": "HelperOutput", + "toNamespace": "App\\\\CouplingExamples\\\\Library\\\\HelperOutput", + "toSource": "Library\\\\HelperOutput.php", + "usageType": "usage", + }, + Object { + "fromClassName": "AnotherControllerOne", + "fromNamespace": "App\\\\CouplingExamplesOne\\\\AnotherControllerOne", + "fromSource": "AnotherController.php", + "toClassName": "FastControllerInterface", + "toNamespace": "App\\\\FastControllerInterface", + "toSource": "FastControllerInterface.php", + "usageType": "implements", + }, + ], +} +`; diff --git a/src/parser/metrics/coupling/CallExpressionResolver.test.ts b/test/parser/metrics/coupling/CallExpressionResolver.test.ts similarity index 92% rename from src/parser/metrics/coupling/CallExpressionResolver.test.ts rename to test/parser/metrics/coupling/CallExpressionResolver.test.ts index 9e8b7281..dcfe159d 100644 --- a/src/parser/metrics/coupling/CallExpressionResolver.test.ts +++ b/test/parser/metrics/coupling/CallExpressionResolver.test.ts @@ -1,7 +1,7 @@ -import { Relationship } from "../Metric"; -import { getAdditionalRelationships } from "./CallExpressionResolver"; -import { UnresolvedCallExpression } from "../../resolver/typeUsages/AbstractCollector"; -import { Accessor } from "../../resolver/callExpressions/AbstractCollector"; +import { Relationship } from "../../../../src/parser/metrics/Metric"; +import { getAdditionalRelationships } from "../../../../src/parser/metrics/coupling/CallExpressionResolver"; +import { UnresolvedCallExpression } from "../../../../src/parser/resolver/typeUsages/AbstractCollector"; +import { Accessor } from "../../../../src/parser/resolver/callExpressions/AbstractCollector"; describe("CallExpressionResolver", () => { describe("resolves call expressions and retrieves additional and transitive relationships", () => {