From 75fad84cacf5029e4588b216b0e5ca0662177716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Gonz=C3=A1lez?= Date: Fri, 22 Aug 2025 17:47:06 +0200 Subject: [PATCH] Tests passing, all formulas validated --- .gitignore | 2 + README.md | 1 - lib/formula_evaluator.dart | 18 +--- lib/formula_models.dart | 18 +++- test/formula_evaluator_test.dart | 166 +++++++++++++++---------------- 5 files changed, 103 insertions(+), 102 deletions(-) diff --git a/.gitignore b/.gitignore index 49669ed..16fb395 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ .local/ .zcompdump .zshrc +.idea +/d4rt-formulas.iml diff --git a/README.md b/README.md index ae5fc20..e7685f0 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ A comprehensive command-line application for managing and computing mathematical This project uses dart language, and flutter framework. It leverages d4rt library to execute formulas. - # Development guidelines If you are a contributor or an agent, please follow [CLAUDE.md](./CLAUDE.md) for development guidelines. diff --git a/lib/formula_evaluator.dart b/lib/formula_evaluator.dart index 617279c..d1bd957 100644 --- a/lib/formula_evaluator.dart +++ b/lib/formula_evaluator.dart @@ -32,7 +32,7 @@ class FormulaEvaluator { /// Evaluates a formula with the given input values. /// - /// The [formula] must have exactly one output variable. + /// The [formula] must have exactly one output variable (validated during construction). /// The [inputValues] map must contain values for all input variables defined /// in the formula. /// @@ -48,11 +48,9 @@ class FormulaEvaluator { /// Returns the computed value of the single output variable. /// /// Throws [FormulaEvaluationException] if: - /// - The formula has zero or more than one output variable /// - Required input variables are missing /// - The d4rt code execution fails dynamic evaluate(Formula formula, Map inputValues) { - _validateFormula(formula); _validateInputValues(formula, inputValues); try { @@ -71,16 +69,6 @@ class FormulaEvaluator { } } - /// Validates that the formula has exactly one output variable - void _validateFormula(Formula formula) { - if (formula.output.length != 1) { - throw FormulaEvaluationException( - 'Formula "${formula.name}" must have exactly one output variable, ' - 'but has ${formula.output.length}', - ); - } - } - /// Validates that all required input variables are provided void _validateInputValues(Formula formula, Map inputValues) { final missingVars = []; @@ -101,13 +89,13 @@ class FormulaEvaluator { /// Gets the name of the single output variable from the formula String getOutputVariableName(Formula formula) { - _validateFormula(formula); + // Formula construction already ensures exactly one output variable return formula.output.keys.first; } /// Gets the magnitude of the single output variable from the formula String getOutputVariableMagnitude(Formula formula) { - _validateFormula(formula); + // Formula construction already ensures exactly one output variable return formula.output.values.first.magnitude; } diff --git a/lib/formula_models.dart b/lib/formula_models.dart index f5088b8..a7f0f5e 100644 --- a/lib/formula_models.dart +++ b/lib/formula_models.dart @@ -46,12 +46,26 @@ class Formula { final Map output; // Supports multiple output variables final String d4rtCode; - const Formula({ + Formula({ required this.name, required this.input, required this.output, required this.d4rtCode, - }); + }){ + validate(); + } + + validate() { + if (name.trim().isEmpty) { + throw ArgumentError('Formula name cannot be empty'); + } + if (output.length != 1) { + throw ArgumentError( + 'Formula "$name" must have exactly one output variable, ' + 'but has ${output.length}', + ); + } + } factory Formula.fromJson(Map json) { final name = json['name']; diff --git a/test/formula_evaluator_test.dart b/test/formula_evaluator_test.dart index 0665c2d..b06bafa 100644 --- a/test/formula_evaluator_test.dart +++ b/test/formula_evaluator_test.dart @@ -18,9 +18,7 @@ void main() { 'm': VariableSpec(magnitude: 'mass'), 'a': VariableSpec(magnitude: 'acceleration'), }, - output: { - 'F': VariableSpec(magnitude: 'force'), - }, + output: {'F': VariableSpec(magnitude: 'force')}, d4rtCode: ''' main() { return a * m; @@ -29,8 +27,8 @@ void main() { ); final result = evaluator.evaluate(formula, { - 'm': 10.0, // 10 kg - 'a': 9.8, // 9.8 m/s² + 'm': 10.0, // 10 kg + 'a': 9.8, // 9.8 m/s² }); expect(result, 98.0); // F = m * a = 10 * 9.8 = 98 N @@ -43,9 +41,7 @@ void main() { 'x': VariableSpec(magnitude: 'scalar'), 'y': VariableSpec(magnitude: 'scalar'), }, - output: { - 'result': VariableSpec(magnitude: 'scalar'), - }, + output: {'result': VariableSpec(magnitude: 'scalar')}, d4rtCode: ''' main() { return x + y; @@ -53,10 +49,7 @@ void main() { ''', ); - final result = evaluator.evaluate(formula, { - 'x': 5, - 'y': 3, - }); + final result = evaluator.evaluate(formula, {'x': 5, 'y': 3}); expect(result, 8); }); @@ -64,12 +57,8 @@ void main() { test('handles single input variable', () { final formula = Formula( name: 'Square function', - input: { - 'n': VariableSpec(magnitude: 'scalar'), - }, - output: { - 'result': VariableSpec(magnitude: 'scalar'), - }, + input: {'n': VariableSpec(magnitude: 'scalar')}, + output: {'result': VariableSpec(magnitude: 'scalar')}, d4rtCode: ''' main() { return n * n; @@ -89,9 +78,7 @@ void main() { 'b': VariableSpec(magnitude: 'scalar'), 'c': VariableSpec(magnitude: 'scalar'), }, - output: { - 'discriminant': VariableSpec(magnitude: 'scalar'), - }, + output: {'discriminant': VariableSpec(magnitude: 'scalar')}, d4rtCode: ''' main() { return b * b - 4 * a * c; @@ -99,11 +86,7 @@ void main() { ''', ); - final result = evaluator.evaluate(formula, { - 'a': 1, - 'b': 5, - 'c': 6, - }); + final result = evaluator.evaluate(formula, {'a': 1, 'b': 5, 'c': 6}); expect(result, 1); // b² - 4ac = 25 - 24 = 1 }); @@ -118,9 +101,7 @@ void main() { 'a': VariableSpec(magnitude: 'scalar'), 'b': VariableSpec(magnitude: 'scalar'), }, - output: { - 'result': VariableSpec(magnitude: 'scalar'), - }, + output: {'result': VariableSpec(magnitude: 'scalar')}, d4rtCode: 'main() { return a + b + z; }', ); @@ -136,9 +117,7 @@ void main() { 'a': VariableSpec(magnitude: 'scalar'), 'y': VariableSpec(magnitude: 'scalar'), }, - output: { - 'result': VariableSpec(magnitude: 'scalar'), - }, + output: {'result': VariableSpec(magnitude: 'scalar')}, d4rtCode: ''' main() { // Variables: a=1, y=2, z=3 @@ -147,47 +126,44 @@ void main() { ''', ); - final result = evaluator.evaluate(formula, { - 'z': 3, - 'a': 1, - 'y': 2, - }); + final result = evaluator.evaluate(formula, {'z': 3, 'a': 1, 'y': 2}); expect(result, 123); // 1*100 + 2*10 + 3 = 123 }); }); group('Error handling', () { - test('throws exception for formula with no output variables', () { - final formula = Formula( - name: 'Invalid formula', - input: {'x': VariableSpec(magnitude: 'scalar')}, - output: {}, // No output variables - d4rtCode: 'main() { return x; }', - ); + test( + 'throws exception for formula with no output variables during construction', + () { + expect(() { + return Formula( + name: 'Invalid formula', + input: {'x': VariableSpec(magnitude: 'scalar')}, + output: {}, // No output variables + d4rtCode: 'main() { return x; }', + ); + }, throwsA(isA())); + }, + ); - expect( - () => evaluator.evaluate(formula, {'x': 1}), - throwsA(isA()), - ); - }); - - test('throws exception for formula with multiple output variables', () { - final formula = Formula( - name: 'Invalid formula', - input: {'x': VariableSpec(magnitude: 'scalar')}, - output: { - 'y': VariableSpec(magnitude: 'scalar'), - 'z': VariableSpec(magnitude: 'scalar'), - }, - d4rtCode: 'main(x) { return x; }', - ); - - expect( - () => evaluator.evaluate(formula, {'x': 1}), - throwsA(isA()), - ); - }); + test( + 'throws exception for formula with multiple output variables during construction', + () { + expect( + () => Formula( + name: 'Invalid formula', + input: {'x': VariableSpec(magnitude: 'scalar')}, + output: { + 'y': VariableSpec(magnitude: 'scalar'), + 'z': VariableSpec(magnitude: 'scalar'), + }, + d4rtCode: 'main() { return x; }', + ), + throwsA(isA()), + ); + }, + ); test('throws exception for missing input variables', () { final formula = Formula( @@ -233,33 +209,55 @@ void main() { expect(evaluator.getOutputVariableName(formula), 'force'); }); - test('getOutputVariableMagnitude returns the output variable magnitude', () { - final formula = Formula( - name: 'Test', + test( + 'getOutputVariableMagnitude returns the output variable magnitude', + () { + final formula = Formula( + name: 'Test', + input: {'x': VariableSpec(magnitude: 'scalar')}, + output: {'force': VariableSpec(magnitude: 'Newton')}, + d4rtCode: 'main() { return x; }', + ); + + expect(evaluator.getOutputVariableMagnitude(formula), 'Newton'); + }, + ); + + test('utility methods work correctly with valid formulas', () { + final validFormula = Formula( + name: 'Valid Formula', input: {'x': VariableSpec(magnitude: 'scalar')}, - output: {'force': VariableSpec(magnitude: 'Newton')}, + output: {'result': VariableSpec(magnitude: 'Newton')}, d4rtCode: 'main() { return x; }', ); - expect(evaluator.getOutputVariableMagnitude(formula), 'Newton'); + expect(evaluator.getOutputVariableName(validFormula), 'result'); + expect(evaluator.getOutputVariableMagnitude(validFormula), 'Newton'); }); - test('utility methods throw exception for invalid formulas', () { - final invalidFormula = Formula( - name: 'Invalid', - input: {'x': VariableSpec(magnitude: 'scalar')}, - output: {}, // No output variables - d4rtCode: 'main() { return x; }', + test('validates formula construction with factory method', () { + // Test the factory method validation + expect( + () => Formula( + name: 'Invalid', + input: {'x': VariableSpec(magnitude: 'scalar')}, + output: {}, // No output variables + d4rtCode: 'main() { return x; }', + ), + throwsA(isA()), ); expect( - () => evaluator.getOutputVariableName(invalidFormula), - throwsA(isA()), - ); - - expect( - () => evaluator.getOutputVariableMagnitude(invalidFormula), - throwsA(isA()), + () => Formula( + name: 'Invalid', + input: {'x': VariableSpec(magnitude: 'scalar')}, + output: { + 'y': VariableSpec(magnitude: 'scalar'), + 'z': VariableSpec(magnitude: 'scalar'), + }, + d4rtCode: 'main() { return x; }', + ), + throwsA(isA()), ); }); });