ivprogAssessment.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import { Decimal } from 'decimal.js';
  2. import line_i18n from 'line-i18n'
  3. import { IVProgParser } from "./../ast/ivprogParser";
  4. import { SemanticAnalyser } from "./../processor/semantic/semanticAnalyser";
  5. import { IVProgProcessor } from "./../processor/ivprogProcessor";
  6. import { InputTest } from "./../util/inputTest";
  7. import { OutputTest } from "./../util/outputTest";
  8. import { DOMConsole} from "./../io/domConsole";
  9. import * as LocalizedStringsService from "../services/localizedStringsService";
  10. import { Config } from "../util/config";
  11. const LocalizedStrings = LocalizedStringsService.getInstance();
  12. const StringTypes = line_i18n.StringTypes;
  13. export class IVProgAssessment {
  14. constructor (textCode, testCases, domConsole) {
  15. this.textCode = textCode;
  16. this.testCases = testCases;
  17. this.domConsole = domConsole;
  18. }
  19. runTest () {
  20. try {
  21. // try and show error messages through domconsole
  22. const parser = IVProgParser.createParser(this.textCode);
  23. const semantic = new SemanticAnalyser(parser.parseTree());
  24. const validTree = semantic.analyseTree();
  25. // loop test cases and show messages through domconsole
  26. const partialTests = this.testCases.map( (t, name) => {
  27. return this.partialEvaluateTestCase(new IVProgProcessor(validTree), t.input, t.output, name);
  28. });
  29. const testResult = partialTests.reduce((acc, curr) => acc.then(curr), Promise.resolve(0));
  30. return testResult.then(total => {
  31. const grade = total / this.testCases.length;
  32. const channel = grade == 1 ? DOMConsole.INFO : DOMConsole.ERR;
  33. this.writeToConsole(channel, StringTypes.MESSAGE, "test_suite_grade", grade * 100);
  34. return Promise.resolve(grade)
  35. }).catch(err => {
  36. this.domConsole.err("Erro inesperado durante o cálculo da nota.");// try and show error messages through domconsole
  37. this.domConsole.err(err.message);
  38. return Promise.resolve(0);
  39. });
  40. } catch (error) {
  41. this.domConsole.err("Erro inesperado durante a execução do programa");// try and show error messages through domconsole
  42. this.domConsole.err(error.message);
  43. return Promise.resolve(0);
  44. }
  45. }
  46. evaluateTestCase (prog, inputList, expectedOutputs, name, accumulator) {
  47. const outerThis = this;
  48. const input = new InputTest(inputList);
  49. const output = new OutputTest();
  50. prog.registerInput(input);
  51. prog.registerOutput(output);
  52. const startTime = Date.now()
  53. return prog.interpretAST().then( _ => {
  54. const millis = Date.now() - startTime;
  55. if (input.inputList.length !== input.index) {
  56. outerThis.showErrorMessage('test_case_few_reads', name+1);
  57. outerThis.showInfoMessage('test_case_duration', millis);
  58. return Promise.resolve(accumulator + (input.index/inputList.length));
  59. } else if (output.list.length != expectedOutputs.length) {
  60. outerThis.showErrorMessage('test_case_failed', name + 1, inputList.join(','),
  61. expectedOutputs.join(','), output.list.join(','));
  62. outerThis.showInfoMessage('test_case_duration', millis);
  63. // must check for a partial match of the generated output
  64. const numMatchedOutputs = output.list.reduce((acc, actualOutput, index) => {
  65. if(outerThis.checkOutputValues(actualOutput, expectedOutputs[index])) {
  66. return acc + 1;
  67. } else {
  68. return acc;
  69. }
  70. }, 0);
  71. const maxLength = Math.max(expectedOutputs.length, output.list.length);
  72. return Promise.resolve(accumulator + (numMatchedOutputs/maxLength));
  73. } else {
  74. const isOk = outerThis.checkOutputLists(output.list, expectedOutputs);
  75. if(!isOk) {
  76. console.log("not ok.");
  77. outerThis.showErrorMessage('test_case_failed', name + 1, inputList.join(','),
  78. expectedOutputs.join(','), output.list.join(','));
  79. outerThis.showInfoMessage('test_case_duration', millis);
  80. return Promise.resolve(accumulator);
  81. } else {
  82. outerThis.showInfoMessage('test_case_success', name + 1);
  83. outerThis.showInfoMessage('test_case_duration', millis);
  84. return Promise.resolve(accumulator + 1);
  85. }
  86. }
  87. }).catch( error => {
  88. outerThis.showErrorMessage('test_case_failed_exception', name + 1, error.message);
  89. return Promise.resolve(accumulator);
  90. });
  91. }
  92. partialEvaluateTestCase (prog, inputList, expectedOutputs, name) {
  93. return this.evaluateTestCase.bind(this, prog, inputList, expectedOutputs, name);
  94. }
  95. checkOutputLists (actualOutputs, expectedOutputs) {
  96. for (let i = 0; i < actualOutputs.length; i++) {
  97. const outValue = actualOutputs[i];
  98. const expectedValue = expectedOutputs[i];
  99. if(!this.checkOutputValues(outValue, expectedValue)) {
  100. return false;
  101. }
  102. }
  103. return true;
  104. }
  105. checkOutputValues (actualValue, expectedValue) {
  106. let castNumberA = parseFloat(actualValue);
  107. if(!Number.isNaN(castNumberA)) {
  108. let castNumberB = parseFloat(expectedValue);
  109. if(Number.isNaN(castNumberB)) {
  110. return false;
  111. }
  112. castNumberA = new Decimal(castNumberA);
  113. castNumberB = new Decimal(castNumberB);
  114. const decimalPlaces = Math.min(castNumberB.dp(), Config.decimalPlaces);
  115. Decimal.set({ rounding: Decimal.ROUND_FLOOR});
  116. castNumberA = new Decimal(castNumberA.toFixed(decimalPlaces));
  117. castNumberB = new Decimal(castNumberB.toFixed(decimalPlaces));
  118. const aEqualsB = castNumberA.eq(castNumberB);
  119. Decimal.set({ rounding: Decimal.ROUND_HALF_UP});
  120. if (!aEqualsB) {
  121. return false;
  122. }
  123. } else if(actualValue != expectedValue) {
  124. return false;
  125. }
  126. return true;
  127. }
  128. showErrorMessage (errorID, ...args) {
  129. this.domConsole.err(LocalizedStrings.getError(errorID, args));
  130. }
  131. showInfoMessage (msgID, ...args) {
  132. this.domConsole.info(LocalizedStrings.getMessage(msgID, args));
  133. }
  134. writeToConsole (channel, msgType, msgID, ...args) {
  135. let msg = LocalizedStrings.getString(msgID, msgType);
  136. msg = LocalizedStrings.processString(msg, args);
  137. switch(channel) {
  138. case DOMConsole.ERR: {
  139. this.domConsole.err(msg);
  140. break;
  141. }
  142. case DOMConsole.INFO: {
  143. this.domConsole.info(msg);
  144. break;
  145. }
  146. case DOMConsole.USER: {
  147. this.domConsole.write(msg);
  148. break;
  149. }
  150. }
  151. }
  152. }