ivprogAssessment.js 7.6 KB

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