|
@@ -1,14 +1,18 @@
|
|
-import { IVProgParser } from "./../ast/ivprogParser";
|
|
|
|
|
|
+import { Decimal } from 'decimal.js';
|
|
|
|
+import line_i18n from 'line-i18n'
|
|
import { SemanticAnalyser } from "./../processor/semantic/semanticAnalyser";
|
|
import { SemanticAnalyser } from "./../processor/semantic/semanticAnalyser";
|
|
import { IVProgProcessor } from "./../processor/ivprogProcessor";
|
|
import { IVProgProcessor } from "./../processor/ivprogProcessor";
|
|
import { InputTest } from "./../util/inputTest";
|
|
import { InputTest } from "./../util/inputTest";
|
|
import { OutputTest } from "./../util/outputTest";
|
|
import { OutputTest } from "./../util/outputTest";
|
|
|
|
+import { DOMConsole} from "./../io/domConsole";
|
|
import * as LocalizedStringsService from "../services/localizedStringsService";
|
|
import * as LocalizedStringsService from "../services/localizedStringsService";
|
|
-import { Decimal } from 'decimal.js';
|
|
|
|
import { Config } from "../util/config";
|
|
import { Config } from "../util/config";
|
|
|
|
|
|
|
|
+
|
|
const LocalizedStrings = LocalizedStringsService.getInstance();
|
|
const LocalizedStrings = LocalizedStringsService.getInstance();
|
|
|
|
|
|
|
|
+const StringTypes = line_i18n.StringTypes;
|
|
|
|
+
|
|
export class IVProgAssessment {
|
|
export class IVProgAssessment {
|
|
|
|
|
|
constructor (textCode, testCases, domConsole) {
|
|
constructor (textCode, testCases, domConsole) {
|
|
@@ -19,29 +23,30 @@ export class IVProgAssessment {
|
|
|
|
|
|
runTest () {
|
|
runTest () {
|
|
try {
|
|
try {
|
|
- // try and show error messages through domconsole
|
|
|
|
- const parser = IVProgParser.createParser(this.textCode);
|
|
|
|
- const semantic = new SemanticAnalyser(parser.parseTree());
|
|
|
|
- const validTree = semantic.analyseTree();
|
|
|
|
|
|
+ const validTree = SemanticAnalyser.analyseFromSource(this.textCode);
|
|
// loop test cases and show messages through domconsole
|
|
// loop test cases and show messages through domconsole
|
|
const partialTests = this.testCases.map( (t, name) => {
|
|
const partialTests = this.testCases.map( (t, name) => {
|
|
return this.partialEvaluateTestCase(new IVProgProcessor(validTree), t.input, t.output, name);
|
|
return this.partialEvaluateTestCase(new IVProgProcessor(validTree), t.input, t.output, name);
|
|
});
|
|
});
|
|
const testResult = partialTests.reduce((acc, curr) => acc.then(curr), Promise.resolve(0));
|
|
const testResult = partialTests.reduce((acc, curr) => acc.then(curr), Promise.resolve(0));
|
|
- return testResult.then(total => Promise.resolve(total / this.testCases.length))
|
|
|
|
- .catch(err => {
|
|
|
|
- this.domConsole.err("Erro durante a execução do programa");// try and show error messages through domconsole
|
|
|
|
|
|
+ return testResult.then(total => {
|
|
|
|
+ const grade = total / this.testCases.length;
|
|
|
|
+ const channel = grade == 1 ? DOMConsole.INFO : DOMConsole.ERR;
|
|
|
|
+ this.writeToConsole(channel, StringTypes.MESSAGE, "test_suite_grade", grade * 100);
|
|
|
|
+ return Promise.resolve(grade)
|
|
|
|
+ }).catch(err => {
|
|
|
|
+ this.domConsole.err("Erro inesperado durante o cálculo da nota.");// try and show error messages through domconsole
|
|
this.domConsole.err(err.message);
|
|
this.domConsole.err(err.message);
|
|
return Promise.resolve(0);
|
|
return Promise.resolve(0);
|
|
});
|
|
});
|
|
} catch (error) {
|
|
} catch (error) {
|
|
- this.domConsole.err("Erro durante a execução do programa");// try and show error messages through domconsole
|
|
|
|
|
|
+ this.domConsole.err("Erro inesperado durante a execução do programa");// try and show error messages through domconsole
|
|
this.domConsole.err(error.message);
|
|
this.domConsole.err(error.message);
|
|
return Promise.resolve(0);
|
|
return Promise.resolve(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- evaluateTestCase (prog, inputList, outputList, name, accumulator) {
|
|
|
|
|
|
+ evaluateTestCase (prog, inputList, expectedOutputs, name, accumulator) {
|
|
const outerThis = this;
|
|
const outerThis = this;
|
|
const input = new InputTest(inputList);
|
|
const input = new InputTest(inputList);
|
|
const output = new OutputTest();
|
|
const output = new OutputTest();
|
|
@@ -52,28 +57,33 @@ export class IVProgAssessment {
|
|
const millis = Date.now() - startTime;
|
|
const millis = Date.now() - startTime;
|
|
if (input.inputList.length !== input.index) {
|
|
if (input.inputList.length !== input.index) {
|
|
outerThis.showErrorMessage('test_case_few_reads', name+1);
|
|
outerThis.showErrorMessage('test_case_few_reads', name+1);
|
|
- outerThis.showMessage('test_case_duration', millis);
|
|
|
|
- return Promise.resolve(accumulator + 1 * (input.index/inputList.length));
|
|
|
|
- } else if (output.list.length < outputList.length) {
|
|
|
|
- outerThis.showErrorMessage('test_case_failed', name + 1, inputList.join(','),
|
|
|
|
- outputList.join(','), output.list.join(','));
|
|
|
|
- outerThis.showMessage('test_case_duration', millis);
|
|
|
|
- return Promise.resolve(accumulator + 1 * (output.list.length/outputList.length));
|
|
|
|
- } else if (output.list.length > outputList.length) {
|
|
|
|
|
|
+ outerThis.showInfoMessage('test_case_duration', millis);
|
|
|
|
+ return Promise.resolve(accumulator + (input.index/inputList.length));
|
|
|
|
+ } else if (output.list.length != expectedOutputs.length) {
|
|
outerThis.showErrorMessage('test_case_failed', name + 1, inputList.join(','),
|
|
outerThis.showErrorMessage('test_case_failed', name + 1, inputList.join(','),
|
|
- outputList.join(','), output.list.join(','));
|
|
|
|
- outerThis.showMessage('test_case_duration', millis);
|
|
|
|
- return Promise.resolve(accumulator + 1 * (outputList.length/output.list.length));
|
|
|
|
|
|
+ expectedOutputs.join(','), output.list.join(','));
|
|
|
|
+ outerThis.showInfoMessage('test_case_duration', millis);
|
|
|
|
+ // must check for a partial match of the generated output
|
|
|
|
+ const numMatchedOutputs = output.list.reduce((acc, actualOutput, index) => {
|
|
|
|
+ if(outerThis.checkOutputValues(actualOutput, expectedOutputs[index])) {
|
|
|
|
+ return acc + 1;
|
|
|
|
+ } else {
|
|
|
|
+ return acc;
|
|
|
|
+ }
|
|
|
|
+ }, 0);
|
|
|
|
+ const maxLength = Math.max(expectedOutputs.length, output.list.length);
|
|
|
|
+ return Promise.resolve(accumulator + (numMatchedOutputs/maxLength));
|
|
} else {
|
|
} else {
|
|
- const isOk = outerThis.checkOutput(output.list, outputList);
|
|
|
|
|
|
+ const isOk = outerThis.checkOutputLists(output.list, expectedOutputs);
|
|
if(!isOk) {
|
|
if(!isOk) {
|
|
|
|
+ console.log("not ok.");
|
|
outerThis.showErrorMessage('test_case_failed', name + 1, inputList.join(','),
|
|
outerThis.showErrorMessage('test_case_failed', name + 1, inputList.join(','),
|
|
- outputList.join(','), output.list.join(','));
|
|
|
|
- outerThis.showMessage('test_case_duration', millis);
|
|
|
|
|
|
+ expectedOutputs.join(','), output.list.join(','));
|
|
|
|
+ outerThis.showInfoMessage('test_case_duration', millis);
|
|
return Promise.resolve(accumulator);
|
|
return Promise.resolve(accumulator);
|
|
} else {
|
|
} else {
|
|
- outerThis.showMessage('test_case_success', name + 1);
|
|
|
|
- outerThis.showMessage('test_case_duration', millis);
|
|
|
|
|
|
+ outerThis.showInfoMessage('test_case_success', name + 1);
|
|
|
|
+ outerThis.showInfoMessage('test_case_duration', millis);
|
|
return Promise.resolve(accumulator + 1);
|
|
return Promise.resolve(accumulator + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -83,42 +93,69 @@ export class IVProgAssessment {
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
- partialEvaluateTestCase (prog, inputList, outputList, name) {
|
|
|
|
- return this.evaluateTestCase.bind(this, prog, inputList, outputList, name);
|
|
|
|
|
|
+ partialEvaluateTestCase (prog, inputList, expectedOutputs, name) {
|
|
|
|
+ return this.evaluateTestCase.bind(this, prog, inputList, expectedOutputs, name);
|
|
}
|
|
}
|
|
|
|
|
|
- checkOutput (aList, bList) {
|
|
|
|
- for (let i = 0; i < aList.length; i++) {
|
|
|
|
- const outValue = aList[i];
|
|
|
|
- let castNumberA = parseFloat(outValue);
|
|
|
|
- if(!Number.isNaN(castNumberA)) {
|
|
|
|
- let castNumberB = parseFloat(bList[i]);
|
|
|
|
- if(Number.isNaN(castNumberB)) {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- castNumberA = new Decimal(castNumberA);
|
|
|
|
- castNumberB = new Decimal(castNumberB);
|
|
|
|
- const decimalPlaces = Math.min(castNumberB.dp(), Config.decimalPlaces);
|
|
|
|
- Decimal.set({ rounding: Decimal.ROUND_FLOOR});
|
|
|
|
- castNumberA = new Decimal(castNumberA.toFixed(decimalPlaces));
|
|
|
|
- castNumberB = new Decimal(castNumberB.toFixed(decimalPlaces));
|
|
|
|
- const aEqualsB = castNumberA.eq(castNumberB);
|
|
|
|
- Decimal.set({ rounding: Decimal.ROUND_HALF_UP});
|
|
|
|
- if (!aEqualsB) {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- } else if(outValue != bList[i]) {
|
|
|
|
|
|
+ checkOutputLists (actualOutputs, expectedOutputs) {
|
|
|
|
+ for (let i = 0; i < actualOutputs.length; i++) {
|
|
|
|
+ const outValue = actualOutputs[i];
|
|
|
|
+ const expectedValue = expectedOutputs[i];
|
|
|
|
+ if(!this.checkOutputValues(outValue, expectedValue)) {
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ checkOutputValues (actualValue, expectedValue) {
|
|
|
|
+ let castNumberA = parseFloat(actualValue);
|
|
|
|
+ if(!Number.isNaN(castNumberA)) {
|
|
|
|
+ let castNumberB = parseFloat(expectedValue);
|
|
|
|
+ if(Number.isNaN(castNumberB)) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ castNumberA = new Decimal(castNumberA);
|
|
|
|
+ castNumberB = new Decimal(castNumberB);
|
|
|
|
+ const decimalPlaces = Math.min(castNumberB.dp(), Config.decimalPlaces);
|
|
|
|
+ Decimal.set({ rounding: Decimal.ROUND_FLOOR});
|
|
|
|
+ castNumberA = new Decimal(castNumberA.toFixed(decimalPlaces));
|
|
|
|
+ castNumberB = new Decimal(castNumberB.toFixed(decimalPlaces));
|
|
|
|
+ const aEqualsB = castNumberA.eq(castNumberB);
|
|
|
|
+ Decimal.set({ rounding: Decimal.ROUND_HALF_UP});
|
|
|
|
+ if (!aEqualsB) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ } else if(actualValue != expectedValue) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
showErrorMessage (errorID, ...args) {
|
|
showErrorMessage (errorID, ...args) {
|
|
this.domConsole.err(LocalizedStrings.getError(errorID, args));
|
|
this.domConsole.err(LocalizedStrings.getError(errorID, args));
|
|
}
|
|
}
|
|
|
|
|
|
- showMessage (msgID, ...args) {
|
|
|
|
|
|
+ showInfoMessage (msgID, ...args) {
|
|
this.domConsole.info(LocalizedStrings.getMessage(msgID, args));
|
|
this.domConsole.info(LocalizedStrings.getMessage(msgID, args));
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ writeToConsole (channel, msgType, msgID, ...args) {
|
|
|
|
+ let msg = LocalizedStrings.getString(msgID, msgType);
|
|
|
|
+ msg = LocalizedStrings.processString(msg, args);
|
|
|
|
+ switch(channel) {
|
|
|
|
+ case DOMConsole.ERR: {
|
|
|
|
+ this.domConsole.err(msg);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ case DOMConsole.INFO: {
|
|
|
|
+ this.domConsole.info(msg);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ case DOMConsole.USER: {
|
|
|
|
+ this.domConsole.write(msg);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|