From 1225f0c10f4f5d42017f0ed349b7e044412fc9e4 Mon Sep 17 00:00:00 2001 From: Rafed Muhammad Yasir Date: Wed, 16 Oct 2024 16:41:01 -0700 Subject: [PATCH] Fix tests --- .../{SourceParser.ts => ParseSource.ts} | 0 codemetrica/interface/ICodeBlock.ts | 6 +++ codemetrica/interface/IFile.ts | 2 - codemetrica/interface/IFunction.ts | 2 +- .../language/java/metric/MethodLength.ts | 11 +---- codemetrica/language/python/PyClass.ts | 4 +- codemetrica/language/python/PyCodeBlock.ts | 7 ++-- codemetrica/language/python/PyFile.ts | 4 +- codemetrica/language/python/PyFunction.ts | 2 +- codemetrica/language/python/PyParser.ts | 4 +- .../python/smell/ComplexConditional.ts | 4 +- codemetrica/smell/longMethod.ts | 4 +- .../PythonLexer.g4 | 0 .../PythonLexer.interp | 0 .../PythonLexer.tokens | 0 .../PythonLexer.ts | 0 .../PythonLexerBase.ts | 0 .../PythonParser.g4 | 0 .../PythonParser.interp | 0 .../PythonParser.tokens | 0 .../PythonParser.ts | 0 .../PythonParserBase.ts | 0 .../PythonParserListener.ts | 0 .../PythonParserVisitor.ts | 0 .../complex_conditional.py | 18 +++----- .../complex_conditional.test.ts | 27 ++++++------ .../python/smells/long_method/long_method.py | 41 +++++++++++++++++++ .../smells/long_method/long_method.test.ts | 21 ++++++++++ 28 files changed, 105 insertions(+), 52 deletions(-) rename codemetrica/{SourceParser.ts => ParseSource.ts} (100%) create mode 100644 codemetrica/interface/ICodeBlock.ts rename grammars-v4/python/{python3_12_1 => python3_12}/PythonLexer.g4 (100%) rename grammars-v4/python/{python3_12_1 => python3_12}/PythonLexer.interp (100%) rename grammars-v4/python/{python3_12_1 => python3_12}/PythonLexer.tokens (100%) rename grammars-v4/python/{python3_12_1 => python3_12}/PythonLexer.ts (100%) rename grammars-v4/python/{python3_12_1 => python3_12}/PythonLexerBase.ts (100%) rename grammars-v4/python/{python3_12_1 => python3_12}/PythonParser.g4 (100%) rename grammars-v4/python/{python3_12_1 => python3_12}/PythonParser.interp (100%) rename grammars-v4/python/{python3_12_1 => python3_12}/PythonParser.tokens (100%) rename grammars-v4/python/{python3_12_1 => python3_12}/PythonParser.ts (100%) rename grammars-v4/python/{python3_12_1 => python3_12}/PythonParserBase.ts (100%) rename grammars-v4/python/{python3_12_1 => python3_12}/PythonParserListener.ts (100%) rename grammars-v4/python/{python3_12_1 => python3_12}/PythonParserVisitor.ts (100%) create mode 100644 tests/python/smells/long_method/long_method.py create mode 100644 tests/python/smells/long_method/long_method.test.ts diff --git a/codemetrica/SourceParser.ts b/codemetrica/ParseSource.ts similarity index 100% rename from codemetrica/SourceParser.ts rename to codemetrica/ParseSource.ts diff --git a/codemetrica/interface/ICodeBlock.ts b/codemetrica/interface/ICodeBlock.ts new file mode 100644 index 0000000..1ebd029 --- /dev/null +++ b/codemetrica/interface/ICodeBlock.ts @@ -0,0 +1,6 @@ +import { Language } from "../language"; + +export interface ICodeBlock { + ctx: any; + language: Language +} \ No newline at end of file diff --git a/codemetrica/interface/IFile.ts b/codemetrica/interface/IFile.ts index 62a3fad..41c4873 100644 --- a/codemetrica/interface/IFile.ts +++ b/codemetrica/interface/IFile.ts @@ -1,5 +1,3 @@ -import { Language } from "../language"; - export interface IFile { filePath: string | undefined; } \ No newline at end of file diff --git a/codemetrica/interface/IFunction.ts b/codemetrica/interface/IFunction.ts index 5766302..1616824 100644 --- a/codemetrica/interface/IFunction.ts +++ b/codemetrica/interface/IFunction.ts @@ -1,4 +1,4 @@ -import { ICodeBlock } from './IFile'; +import { ICodeBlock } from './ICodeBlock'; import { IParameter } from './IParameter'; export interface IFunction extends ICodeBlock{ diff --git a/codemetrica/language/java/metric/MethodLength.ts b/codemetrica/language/java/metric/MethodLength.ts index 110671a..d66438e 100644 --- a/codemetrica/language/java/metric/MethodLength.ts +++ b/codemetrica/language/java/metric/MethodLength.ts @@ -1,17 +1,10 @@ import { BlockStatementsContext } from '../../../../grammars-v4/java/java20/Java20Parser'; -import { IMethod } from '../../../interface/IMethod'; import { JavaMethod } from '../JavaMethod'; export class MethodLength { - staticcalculate(method: IMethod): number { + calculate(method: JavaMethod): number { const block_statements_ctx: BlockStatementsContext = method.ctx.methodBody().block().blockStatements() - const method_length = block_statements_ctx.stop.line - block_statements_ctx.start.line + 1 + const method_length = block_statements_ctx.stop!.line - block_statements_ctx.start.line + 1 return method_length; } -} - -export function calculateMethodLength(method: JavaMethod): number { - const block_statements_ctx: BlockStatementsContext = method.ctx.methodBody().block().blockStatements() - const method_length = block_statements_ctx.stop.line - block_statements_ctx.start.line + 1 - return method_length; } \ No newline at end of file diff --git a/codemetrica/language/python/PyClass.ts b/codemetrica/language/python/PyClass.ts index f97a6bd..b1c751c 100644 --- a/codemetrica/language/python/PyClass.ts +++ b/codemetrica/language/python/PyClass.ts @@ -1,7 +1,7 @@ import { PyCodeBlock } from "./PyCodeBlock"; import { PyFunction } from "./PyFunction"; -import { Class_defContext, Function_defContext } from "../../../grammars-v4/python/python3_12_1/PythonParser.js"; -import PythonParserVisitor from "../../../grammars-v4/python/python3_12_1/PythonParserVisitor"; +import { Class_defContext, Function_defContext } from "../../../grammars-v4/python/python3_12/PythonParser.js"; +import PythonParserVisitor from "../../../grammars-v4/python/python3_12/PythonParserVisitor"; import { IClass } from "../../interface/IClass"; export class PyClass extends PyCodeBlock implements IClass { diff --git a/codemetrica/language/python/PyCodeBlock.ts b/codemetrica/language/python/PyCodeBlock.ts index 91608d3..d363573 100644 --- a/codemetrica/language/python/PyCodeBlock.ts +++ b/codemetrica/language/python/PyCodeBlock.ts @@ -1,9 +1,10 @@ -import { AtomContext, Simple_stmtContext, Except_blockContext, Except_star_blockContext, ExpressionContext, Match_stmtContext, Star_expressionContext } from "../../../grammars-v4/python/python3_12_1/PythonParser"; -import PythonParserVisitor from "../../../grammars-v4/python/python3_12_1/PythonParserVisitor"; +import { AtomContext, Simple_stmtContext, Except_blockContext, Except_star_blockContext, ExpressionContext, Match_stmtContext, Star_expressionContext } from "../../../grammars-v4/python/python3_12/PythonParser"; +import PythonParserVisitor from "../../../grammars-v4/python/python3_12/PythonParserVisitor"; import { ParserRuleContext } from 'antlr4'; import { Language } from "../../language"; +import { ICodeBlock } from "../../interface/ICodeBlock"; -export abstract class PyCodeBlock{ +export abstract class PyCodeBlock implements ICodeBlock{ ctx: T language: Language diff --git a/codemetrica/language/python/PyFile.ts b/codemetrica/language/python/PyFile.ts index 9e140c4..9bd58ff 100644 --- a/codemetrica/language/python/PyFile.ts +++ b/codemetrica/language/python/PyFile.ts @@ -1,5 +1,5 @@ -import PythonParserVisitor from "../../../grammars-v4/python/python3_12_1/PythonParserVisitor"; -import { Class_defContext, File_inputContext, Function_defContext } from "../../../grammars-v4/python/python3_12_1/PythonParser.js"; +import PythonParserVisitor from "../../../grammars-v4/python/python3_12/PythonParserVisitor"; +import { Class_defContext, File_inputContext, Function_defContext } from "../../../grammars-v4/python/python3_12/PythonParser.js"; import { PyCodeBlock } from "./PyCodeBlock"; import { PyClass } from "./PyClass"; import { PyFunction } from "./PyFunction"; diff --git a/codemetrica/language/python/PyFunction.ts b/codemetrica/language/python/PyFunction.ts index d5f42b1..438f4bc 100644 --- a/codemetrica/language/python/PyFunction.ts +++ b/codemetrica/language/python/PyFunction.ts @@ -1,5 +1,5 @@ import { PyCodeBlock } from "./PyCodeBlock"; -import { Function_defContext } from "../../../grammars-v4/python/python3_12_1/PythonParser"; +import { Function_defContext } from "../../../grammars-v4/python/python3_12/PythonParser"; import { IParameter } from "../../interface/IParameter"; import { IFunction } from "../../interface/IFunction"; import { IMethod } from "../../interface/IMethod"; diff --git a/codemetrica/language/python/PyParser.ts b/codemetrica/language/python/PyParser.ts index 7426921..beb135a 100644 --- a/codemetrica/language/python/PyParser.ts +++ b/codemetrica/language/python/PyParser.ts @@ -1,6 +1,6 @@ import antlr4 from 'antlr4'; -import PythonLexer from '../../../grammars-v4/python/python3_12_1/PythonLexer'; -import PythonParser from '../../../grammars-v4/python/python3_12_1/PythonParser'; +import PythonLexer from '../../../grammars-v4/python/python3_12/PythonLexer'; +import PythonParser from '../../../grammars-v4/python/python3_12/PythonParser'; export function parsePythonSource(sourceCode: string) { const inputStream = new antlr4.InputStream(sourceCode) as unknown as antlr4.CharStream;; diff --git a/codemetrica/language/python/smell/ComplexConditional.ts b/codemetrica/language/python/smell/ComplexConditional.ts index 82f5031..f8a6e41 100644 --- a/codemetrica/language/python/smell/ComplexConditional.ts +++ b/codemetrica/language/python/smell/ComplexConditional.ts @@ -1,6 +1,6 @@ -import PythonParserVisitor from "../../../../grammars-v4/python/python3_12_1/PythonParserVisitor"; +import PythonParserVisitor from "../../../../grammars-v4/python/python3_12/PythonParserVisitor"; import { Thresholds } from '../../../Thresholds'; -import { ExpressionContext } from "../../../../grammars-v4/python/python3_12_1/PythonParser"; +import { ExpressionContext } from "../../../../grammars-v4/python/python3_12/PythonParser"; export class ComplexConditional { static detect(ctx: ExpressionContext): boolean { diff --git a/codemetrica/smell/longMethod.ts b/codemetrica/smell/longMethod.ts index 85ffeab..bd11518 100644 --- a/codemetrica/smell/longMethod.ts +++ b/codemetrica/smell/longMethod.ts @@ -5,7 +5,7 @@ import { calculateMethodLength as javaCalculateMethodLength } from "../language/ import { MethodLength as PyMethodLength } from "../language/python/metric/MethodLength"; import { Thresholds } from "../Thresholds"; -type MethodLengthCalculator = (method: IMethod | IFunction) => number; +type MethodLengthCalculator = (method: any) => number; export class LongMethod { static detect(method: IMethod | IFunction): boolean { @@ -16,7 +16,7 @@ export class LongMethod { case Language.JAVA: return javaCalculateMethodLength; default: - return () => 0; + throw new Error(`Unsupported language: ${language}`); } } diff --git a/grammars-v4/python/python3_12_1/PythonLexer.g4 b/grammars-v4/python/python3_12/PythonLexer.g4 similarity index 100% rename from grammars-v4/python/python3_12_1/PythonLexer.g4 rename to grammars-v4/python/python3_12/PythonLexer.g4 diff --git a/grammars-v4/python/python3_12_1/PythonLexer.interp b/grammars-v4/python/python3_12/PythonLexer.interp similarity index 100% rename from grammars-v4/python/python3_12_1/PythonLexer.interp rename to grammars-v4/python/python3_12/PythonLexer.interp diff --git a/grammars-v4/python/python3_12_1/PythonLexer.tokens b/grammars-v4/python/python3_12/PythonLexer.tokens similarity index 100% rename from grammars-v4/python/python3_12_1/PythonLexer.tokens rename to grammars-v4/python/python3_12/PythonLexer.tokens diff --git a/grammars-v4/python/python3_12_1/PythonLexer.ts b/grammars-v4/python/python3_12/PythonLexer.ts similarity index 100% rename from grammars-v4/python/python3_12_1/PythonLexer.ts rename to grammars-v4/python/python3_12/PythonLexer.ts diff --git a/grammars-v4/python/python3_12_1/PythonLexerBase.ts b/grammars-v4/python/python3_12/PythonLexerBase.ts similarity index 100% rename from grammars-v4/python/python3_12_1/PythonLexerBase.ts rename to grammars-v4/python/python3_12/PythonLexerBase.ts diff --git a/grammars-v4/python/python3_12_1/PythonParser.g4 b/grammars-v4/python/python3_12/PythonParser.g4 similarity index 100% rename from grammars-v4/python/python3_12_1/PythonParser.g4 rename to grammars-v4/python/python3_12/PythonParser.g4 diff --git a/grammars-v4/python/python3_12_1/PythonParser.interp b/grammars-v4/python/python3_12/PythonParser.interp similarity index 100% rename from grammars-v4/python/python3_12_1/PythonParser.interp rename to grammars-v4/python/python3_12/PythonParser.interp diff --git a/grammars-v4/python/python3_12_1/PythonParser.tokens b/grammars-v4/python/python3_12/PythonParser.tokens similarity index 100% rename from grammars-v4/python/python3_12_1/PythonParser.tokens rename to grammars-v4/python/python3_12/PythonParser.tokens diff --git a/grammars-v4/python/python3_12_1/PythonParser.ts b/grammars-v4/python/python3_12/PythonParser.ts similarity index 100% rename from grammars-v4/python/python3_12_1/PythonParser.ts rename to grammars-v4/python/python3_12/PythonParser.ts diff --git a/grammars-v4/python/python3_12_1/PythonParserBase.ts b/grammars-v4/python/python3_12/PythonParserBase.ts similarity index 100% rename from grammars-v4/python/python3_12_1/PythonParserBase.ts rename to grammars-v4/python/python3_12/PythonParserBase.ts diff --git a/grammars-v4/python/python3_12_1/PythonParserListener.ts b/grammars-v4/python/python3_12/PythonParserListener.ts similarity index 100% rename from grammars-v4/python/python3_12_1/PythonParserListener.ts rename to grammars-v4/python/python3_12/PythonParserListener.ts diff --git a/grammars-v4/python/python3_12_1/PythonParserVisitor.ts b/grammars-v4/python/python3_12/PythonParserVisitor.ts similarity index 100% rename from grammars-v4/python/python3_12_1/PythonParserVisitor.ts rename to grammars-v4/python/python3_12/PythonParserVisitor.ts diff --git a/tests/python/smells/complex_conditional/complex_conditional.py b/tests/python/smells/complex_conditional/complex_conditional.py index 47a04c7..83c9384 100644 --- a/tests/python/smells/complex_conditional/complex_conditional.py +++ b/tests/python/smells/complex_conditional/complex_conditional.py @@ -1,16 +1,8 @@ -def getSimpleStmtements(self): - class Simple_stmtListener(PythonParserListener): - def __init__(self) -> None: - self.statements = [] - def enterSimple_stmt(self, ctx: PythonParser.Simple_stmtContext): - self.statements.append(ctx.getText()) - # return super().enterSimple_stmt(ctx) - - listener = Simple_stmtListener() - walker = ParseTreeWalker() - walker.walk(listener, self.ctx) - +def complexConditional(): if 1 > 2 and 3 > 4 or 8>5 or 5 > 7 and 5 > 6 or 9 > 5 and 5 > 6 or 9 > 5: return True - return listener.statements +def notComplexConditional(): + if a > b: + return True + diff --git a/tests/python/smells/complex_conditional/complex_conditional.test.ts b/tests/python/smells/complex_conditional/complex_conditional.test.ts index 3acd05a..b37b152 100644 --- a/tests/python/smells/complex_conditional/complex_conditional.test.ts +++ b/tests/python/smells/complex_conditional/complex_conditional.test.ts @@ -1,24 +1,25 @@ -import { File } from '../../../../python/file'; import { describe, expect, test, beforeAll } from '@jest/globals'; -import { ComplexConditional } from '../../../../python/smells/complex_conditional'; +import { ComplexConditional } from '../../../../codemetrica/language/python/smell/ComplexConditional'; +import { ParseSource } from '../../../../codemetrica/ParseSource'; +import { PyFile, PyFunction } from '../../../../codemetrica/language/python'; describe('ComplexConditional Detection', () => { + let functions: PyFunction[]; + beforeAll(() => { - + const file = ParseSource.fromFileSync('./tests/python/smells/complex_conditional/complex_conditional.py') as PyFile; + functions = file.getFunctions(); }); test('should detect complex conditionals in the file', () => { - let file = new File('./codemetrica/tests/python/smells/complex_conditional/complex_conditional.py'); - let expressions = file.getExpressions(); + let expressions = functions[0].getExpressions(); const complexConditionals = expressions.filter(e => ComplexConditional.detect(e)); - expect(complexConditionals.length).toBeGreaterThan(0); + expect(complexConditionals.length).toEqual(1); }); - // test('should not detect complex conditionals when none exist', () => { - // const emptyFile = new File('./empty_file.py'); // Assuming this file has no complex conditionals - // const emptyExpressions = emptyFile.getExpressions(); - // const complexConditionals = emptyExpressions.filter(e => ComplexConditional.detect(e)); - - // expect(complexConditionals.length).toBe(0); - // }); + test('should not detect complex conditionals when none exist', () => { + let expressions = functions[1].getExpressions(); + const complexConditionals = expressions.filter(e => ComplexConditional.detect(e)); + expect(complexConditionals.length).toEqual(0); + }); }); \ No newline at end of file diff --git a/tests/python/smells/long_method/long_method.py b/tests/python/smells/long_method/long_method.py new file mode 100644 index 0000000..ee12e11 --- /dev/null +++ b/tests/python/smells/long_method/long_method.py @@ -0,0 +1,41 @@ +def process_order(order_id, user, items): + print(f"Processing order {order_id} for user {user}") + + valid_items = [] + for item in items: + if 'id' in item and 'price' in item and item['price'] > 0: + valid_items.append(item) + else: + print(f"Item {item} is invalid") + + subtotal = sum(item['price'] for item in valid_items) + print(f"Subtotal for order {order_id}: {subtotal}") + + discount = 0 + if subtotal > 100: + discount = subtotal * 0.1 # 10% discount for orders over $100 + print(f"Discount applied: {discount}") + + TAX_RATE = 0.08 # 8% tax + tax = (subtotal - discount) * TAX_RATE + print(f"Tax calculated: {tax}") + + total = subtotal - discount + tax + print(f"Final total for order {order_id}: {total}") + + # 6. Update inventory + for item in valid_items: + print(f"Updating inventory for item {item['id']}") + + # 7. Payment processing (simplified) + print(f"Charging {total} to user {user}") + + # 8. Logging the order + print(f"Order {order_id} processed for user {user}") + print("-" * 40) + + +def notComplexConditional(): + if a > b: + return a + diff --git a/tests/python/smells/long_method/long_method.test.ts b/tests/python/smells/long_method/long_method.test.ts new file mode 100644 index 0000000..5c92227 --- /dev/null +++ b/tests/python/smells/long_method/long_method.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, test, beforeAll } from '@jest/globals'; +import { LongMethod } from '../../../../codemetrica/smell/LongMethod'; +import { ParseSource } from '../../../../codemetrica/ParseSource'; +import { PyFile, PyFunction } from '../../../../codemetrica/language/python'; + +describe('ComplexConditional Detection', () => { + let functions: PyFunction[]; + + beforeAll(() => { + const file = ParseSource.fromFileSync('./tests/python/smells/long_method/long_method.py') as PyFile; + functions = file.getFunctions(); + }); + + test('should detect complex conditionals in the file', () => { + expect(LongMethod.detect(functions[0])).toBe(true); + }); + + test('should not detect complex conditionals when none exist', () => { + expect(LongMethod.detect(functions[1])).toBe(false); + }); +}); \ No newline at end of file