| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | /*
 | 
					
						
							|  |  |  |  |  * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> | 
					
						
							|  |  |  |  |  * All rights reserved. | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * Redistribution and use in source and binary forms, with or without | 
					
						
							|  |  |  |  |  * modification, are permitted provided that the following conditions are met: | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * 1. Redistributions of source code must retain the above copyright notice, this | 
					
						
							|  |  |  |  |  *    list of conditions and the following disclaimer. | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | 
					
						
							|  |  |  |  |  *    this list of conditions and the following disclaimer in the documentation | 
					
						
							|  |  |  |  |  *    and/or other materials provided with the distribution. | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
					
						
							|  |  |  |  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
					
						
							|  |  |  |  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
					
						
							|  |  |  |  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | 
					
						
							|  |  |  |  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
					
						
							|  |  |  |  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | 
					
						
							|  |  |  |  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
					
						
							|  |  |  |  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
					
						
							|  |  |  |  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
					
						
							|  |  |  |  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | #include <AK/JsonObject.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-05 07:50:08 -07:00
										 |  |  |  | #include <AK/JsonValue.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | #include <AK/LogStream.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  | #include <LibCore/ArgsParser.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-05 07:50:08 -07:00
										 |  |  |  | #include <LibCore/File.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | #include <LibJS/Interpreter.h>
 | 
					
						
							|  |  |  |  | #include <LibJS/Lexer.h>
 | 
					
						
							|  |  |  |  | #include <LibJS/Parser.h>
 | 
					
						
							|  |  |  |  | #include <LibJS/Runtime/Array.h>
 | 
					
						
							|  |  |  |  | #include <LibJS/Runtime/GlobalObject.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  | #include <LibJS/Runtime/JSONObject.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | #include <LibJS/Runtime/MarkedValueList.h>
 | 
					
						
							|  |  |  |  | #include <stdlib.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-05 07:50:08 -07:00
										 |  |  |  | #include <sys/time.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | #define TOP_LEVEL_TEST_NAME "__$$TOP_LEVEL$$__"
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // FIXME: Will eventually not be necessary when all tests are converted
 | 
					
						
							|  |  |  |  | Vector<String> tests_to_run = { | 
					
						
							| 
									
										
										
										
											2020-07-04 10:09:48 -07:00
										 |  |  |  |     "builtins/Boolean/Boolean.js", | 
					
						
							|  |  |  |  |     "builtins/Boolean/Boolean.prototype.js", | 
					
						
							|  |  |  |  |     "builtins/Boolean/Boolean.prototype.toString.js", | 
					
						
							|  |  |  |  |     "builtins/Boolean/Boolean.prototype.valueOf.js", | 
					
						
							|  |  |  |  |     "builtins/Date/Date.js", | 
					
						
							|  |  |  |  |     "builtins/Date/Date.now.js", | 
					
						
							|  |  |  |  |     "builtins/Date/Date.prototype.getDate.js", | 
					
						
							|  |  |  |  |     "builtins/Date/Date.prototype.getDay.js", | 
					
						
							|  |  |  |  |     "builtins/Date/Date.prototype.getFullYear.js", | 
					
						
							|  |  |  |  |     "builtins/Date/Date.prototype.getHours.js", | 
					
						
							|  |  |  |  |     "builtins/Date/Date.prototype.getMilliseconds.js", | 
					
						
							|  |  |  |  |     "builtins/Date/Date.prototype.getMinutes.js", | 
					
						
							|  |  |  |  |     "builtins/Date/Date.prototype.getMonth.js", | 
					
						
							|  |  |  |  |     "builtins/Date/Date.prototype.getSeconds.js", | 
					
						
							|  |  |  |  |     "builtins/Date/Date.prototype.getTime.js", | 
					
						
							|  |  |  |  |     "builtins/Error/Error.js", | 
					
						
							|  |  |  |  |     "builtins/Error/Error.prototype.name.js", | 
					
						
							|  |  |  |  |     "builtins/Error/Error.prototype.toString.js", | 
					
						
							|  |  |  |  |     "builtins/Function/Function.js", | 
					
						
							|  |  |  |  |     "builtins/Function/Function.prototype.apply.js", | 
					
						
							|  |  |  |  |     "builtins/Function/Function.prototype.bind.js", | 
					
						
							|  |  |  |  |     "builtins/Function/Function.prototype.call.js", | 
					
						
							|  |  |  |  |     "builtins/Function/Function.prototype.toString.js", | 
					
						
							|  |  |  |  |     "builtins/functions/isFinite.js", | 
					
						
							|  |  |  |  |     "builtins/functions/isNaN.js", | 
					
						
							|  |  |  |  |     "builtins/functions/parseFloat.js", | 
					
						
							|  |  |  |  |     "builtins/Infinity/Infinity.js", | 
					
						
							|  |  |  |  |     "builtins/JSON/JSON.parse.js", | 
					
						
							|  |  |  |  |     "builtins/JSON/JSON.parse-reviver.js", | 
					
						
							|  |  |  |  |     "builtins/JSON/JSON.stringify.js", | 
					
						
							|  |  |  |  |     "builtins/JSON/JSON.stringify-order.js", | 
					
						
							|  |  |  |  |     "builtins/JSON/JSON.stringify-proxy.js", | 
					
						
							|  |  |  |  |     "builtins/JSON/JSON.stringify-replacer.js", | 
					
						
							|  |  |  |  |     "builtins/JSON/JSON.stringify-space.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math-constants.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.abs.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.acosh.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.asinh.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.atanh.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.cbrt.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.ceil.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.clz32.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.cos.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.exp.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.expm1.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.floor.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.log1p.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.max.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.min.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.pow.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.sign.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.sqrt.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.tan.js", | 
					
						
							|  |  |  |  |     "builtins/Math/Math.trunc.js", | 
					
						
							|  |  |  |  |     "builtins/NaN/NaN.js", | 
					
						
							|  |  |  |  |     "builtins/Number/Number.js", | 
					
						
							|  |  |  |  |     "builtins/Number/Number-constants.js", | 
					
						
							|  |  |  |  |     "builtins/Number/Number.isFinite.js", | 
					
						
							|  |  |  |  |     "builtins/Number/Number.isInteger.js", | 
					
						
							|  |  |  |  |     "builtins/Number/Number.isNaN.js", | 
					
						
							|  |  |  |  |     "builtins/Number/Number.isSafeInteger.js", | 
					
						
							|  |  |  |  |     "builtins/Number/Number.parseFloat.js", | 
					
						
							|  |  |  |  |     "builtins/Number/Number.prototype.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.defineProperty.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.entries.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.getOwnPropertyDescriptor.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.getOwnPropertyNames.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.getPrototypeOf.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.is.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.isExtensible.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.keys.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.preventExtensions.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.prototype.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.prototype.constructor.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.prototype.hasOwnProperty.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.prototype.toLocaleString.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.prototype.toString.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.setPrototypeOf.js", | 
					
						
							|  |  |  |  |     "builtins/Object/Object.values.js", | 
					
						
							| 
									
										
										
										
											2020-07-03 22:43:08 -07:00
										 |  |  |  |     "builtins/Proxy/Proxy.js", | 
					
						
							|  |  |  |  |     "builtins/Proxy/Proxy.handler-apply.js", | 
					
						
							|  |  |  |  |     "builtins/Proxy/Proxy.handler-construct.js", | 
					
						
							|  |  |  |  |     "builtins/Proxy/Proxy.handler-defineProperty.js", | 
					
						
							|  |  |  |  |     "builtins/Proxy/Proxy.handler-deleteProperty.js", | 
					
						
							|  |  |  |  |     "builtins/Proxy/Proxy.handler-get.js", | 
					
						
							|  |  |  |  |     "builtins/Proxy/Proxy.handler-getOwnPropertyDescriptor.js", | 
					
						
							|  |  |  |  |     "builtins/Proxy/Proxy.handler-getPrototypeOf.js", | 
					
						
							|  |  |  |  |     "builtins/Proxy/Proxy.handler-has.js", | 
					
						
							|  |  |  |  |     "builtins/Proxy/Proxy.handler-isExtensible.js", | 
					
						
							|  |  |  |  |     "builtins/Proxy/Proxy.handler-preventExtensions.js", | 
					
						
							|  |  |  |  |     "builtins/Proxy/Proxy.handler-set.js", | 
					
						
							| 
									
										
										
										
											2020-07-05 07:50:08 -07:00
										 |  |  |  |     "builtins/Proxy/Proxy.handler-setPrototypeOf.js", | 
					
						
							| 
									
										
										
										
											2020-07-04 15:42:19 +01:00
										 |  |  |  |     "builtins/Reflect/Reflect.apply.js", | 
					
						
							|  |  |  |  |     "builtins/Reflect/Reflect.construct.js", | 
					
						
							|  |  |  |  |     "builtins/Reflect/Reflect.defineProperty.js", | 
					
						
							|  |  |  |  |     "builtins/Reflect/Reflect.deleteProperty.js", | 
					
						
							|  |  |  |  |     "builtins/Reflect/Reflect.get.js", | 
					
						
							|  |  |  |  |     "builtins/Reflect/Reflect.getOwnPropertyDescriptor.js", | 
					
						
							|  |  |  |  |     "builtins/Reflect/Reflect.getPrototypeOf.js", | 
					
						
							|  |  |  |  |     "builtins/Reflect/Reflect.has.js", | 
					
						
							|  |  |  |  |     "builtins/Reflect/Reflect.isExtensible.js", | 
					
						
							|  |  |  |  |     "builtins/Reflect/Reflect.ownKeys.js", | 
					
						
							|  |  |  |  |     "builtins/Reflect/Reflect.preventExtensions.js", | 
					
						
							|  |  |  |  |     "builtins/Reflect/Reflect.set.js", | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     "builtins/Reflect/Reflect.setPrototypeOf.js", | 
					
						
							| 
									
										
										
										
											2020-07-04 10:09:48 -07:00
										 |  |  |  |     "builtins/String/String.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.fromCharCode.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype-generic-functions.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype.charAt.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype.includes.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype.indexOf.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype.lastIndexOf.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype.padEnd.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype.padStart.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype.repeat.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype.slice.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype.startsWith.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype.substring.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype.toLowerCase.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype.toString.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype.toUpperCase.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype.trim.js", | 
					
						
							|  |  |  |  |     "builtins/String/String.prototype.valueOf.js", | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     "builtins/String/String.raw.js", | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |     "add-values-to-primitive.js", | 
					
						
							|  |  |  |  |     "automatic-semicolon-insertion.js", | 
					
						
							|  |  |  |  |     "comments-basic.js", | 
					
						
							| 
									
										
										
										
											2020-07-05 10:47:40 -07:00
										 |  |  |  |     "const-reassignment.js", | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |     "debugger-statement.js", | 
					
						
							|  |  |  |  |     "empty-statements.js", | 
					
						
							|  |  |  |  |     "exception-ReferenceError.js", | 
					
						
							|  |  |  |  |     "exponentiation-basic.js", | 
					
						
							|  |  |  |  |     "indexed-access-string-object.js", | 
					
						
							|  |  |  |  |     "invalid-lhs-in-assignment.js", | 
					
						
							| 
									
										
										
										
											2020-07-03 14:39:25 -07:00
										 |  |  |  |     "let-scoping.js", | 
					
						
							|  |  |  |  |     "new-expression.js", | 
					
						
							|  |  |  |  |     "numeric-literals-basic.js", | 
					
						
							| 
									
										
										
										
											2020-07-05 10:47:40 -07:00
										 |  |  |  |     "object-basic.js", | 
					
						
							| 
									
										
										
										
											2020-07-03 14:39:25 -07:00
										 |  |  |  |     "object-getter-setter-shorthand.js", | 
					
						
							|  |  |  |  |     "object-method-shorthand.js", | 
					
						
							|  |  |  |  |     "object-spread.js", | 
					
						
							| 
									
										
										
										
											2020-07-05 10:47:40 -07:00
										 |  |  |  |     "parser-unary-associativity.js", | 
					
						
							|  |  |  |  |     "program-strict-mode.js", | 
					
						
							|  |  |  |  |     "strict-mode-errors.js", | 
					
						
							|  |  |  |  |     "string-escapes.js", | 
					
						
							|  |  |  |  |     "string-spread.js", | 
					
						
							|  |  |  |  |     "switch-basic.js", | 
					
						
							|  |  |  |  |     "switch-break.js", | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |     "tagged-template-literals.js", | 
					
						
							| 
									
										
										
										
											2020-07-05 10:47:40 -07:00
										 |  |  |  |     "template-literals.js", | 
					
						
							| 
									
										
										
										
											2020-07-03 18:09:35 -07:00
										 |  |  |  |     "test-common-tests.js", | 
					
						
							| 
									
										
										
										
											2020-07-05 10:47:40 -07:00
										 |  |  |  |     "throw-basic.js", | 
					
						
							|  |  |  |  |     "to-number-basic.js", | 
					
						
							|  |  |  |  |     "to-number-exception.js", | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |     "update-expression-on-member-expression.js", | 
					
						
							| 
									
										
										
										
											2020-07-05 10:47:40 -07:00
										 |  |  |  |     "update-expressions-basic.js", | 
					
						
							|  |  |  |  |     "var-multiple-declarator.js", | 
					
						
							|  |  |  |  |     "var-scoping.js", | 
					
						
							|  |  |  |  |     "variable-undefined.js", | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  | enum class TestResult { | 
					
						
							|  |  |  |  |     Pass, | 
					
						
							|  |  |  |  |     Fail, | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |     Skip, | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | struct JSTest { | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |     String name; | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     TestResult result; | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  | struct JSSuite { | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |     String name; | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |     // A failed test takes precedence over a skipped test, which both have
 | 
					
						
							|  |  |  |  |     // precedence over a passed test
 | 
					
						
							|  |  |  |  |     TestResult most_severe_test_result { TestResult::Pass }; | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     Vector<JSTest> tests {}; | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  | struct ParserError { | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |     JS::Parser::Error error; | 
					
						
							|  |  |  |  |     String hint; | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  | struct JSFileResult { | 
					
						
							|  |  |  |  |     String name; | 
					
						
							|  |  |  |  |     Optional<ParserError> error {}; | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  |     double time_taken { 0 }; | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |     // A failed test takes precedence over a skipped test, which both have
 | 
					
						
							|  |  |  |  |     // precedence over a passed test
 | 
					
						
							|  |  |  |  |     TestResult most_severe_test_result { TestResult::Pass }; | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     Vector<JSSuite> suites {}; | 
					
						
							| 
									
										
										
										
											2020-07-04 20:23:46 -07:00
										 |  |  |  |     Vector<String> logged_messages {}; | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | struct JSTestRunnerCounts { | 
					
						
							|  |  |  |  |     int tests_failed { 0 }; | 
					
						
							|  |  |  |  |     int tests_passed { 0 }; | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |     int tests_skipped { 0 }; | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     int suites_failed { 0 }; | 
					
						
							|  |  |  |  |     int suites_passed { 0 }; | 
					
						
							|  |  |  |  |     int files_total { 0 }; | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  | using JSTestRunnerResult = Vector<JSFileResult>; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | class TestRunner { | 
					
						
							|  |  |  |  | public: | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  |     TestRunner(String test_root, bool print_times) | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |         : m_test_root(move(test_root)) | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  |         , m_print_times(print_times) | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     { | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     void run(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | private: | 
					
						
							|  |  |  |  |     JSFileResult run_file_test(const String& test_path); | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  |     void print_file_result(const JSFileResult& file_result) const; | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     void print_test_results() const; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     String m_test_root; | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  |     bool m_print_times; | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  |     double m_total_elapsed_time_in_ms { 0 }; | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     JSTestRunnerCounts m_counts; | 
					
						
							| 
									
										
										
										
											2020-07-04 13:45:20 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     RefPtr<JS::Program> m_test_program; | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-05 10:47:40 -07:00
										 |  |  |  | class TestRunnerGlobalObject : public JS::GlobalObject { | 
					
						
							|  |  |  |  | public: | 
					
						
							|  |  |  |  |     TestRunnerGlobalObject(); | 
					
						
							|  |  |  |  |     virtual ~TestRunnerGlobalObject() override; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     virtual void initialize() override; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | private: | 
					
						
							|  |  |  |  |     virtual const char* class_name() const override { return "TestRunnerGlobalObject"; } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     JS_DECLARE_NATIVE_FUNCTION(is_strict_mode); | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | TestRunnerGlobalObject::TestRunnerGlobalObject() | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | TestRunnerGlobalObject::~TestRunnerGlobalObject() | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | void TestRunnerGlobalObject::initialize() | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     JS::GlobalObject::initialize(); | 
					
						
							|  |  |  |  |     define_property("global", this, JS::Attribute::Enumerable); | 
					
						
							|  |  |  |  |     define_native_function("isStrictMode", is_strict_mode); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | JS_DEFINE_NATIVE_FUNCTION(TestRunnerGlobalObject::is_strict_mode) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     return JS::Value(interpreter.in_strict_mode()); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | double get_time() | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     struct timeval tv1; | 
					
						
							|  |  |  |  |     struct timezone tz1; | 
					
						
							|  |  |  |  |     auto return_code = gettimeofday(&tv1, &tz1); | 
					
						
							|  |  |  |  |         ASSERT(return_code >= 0); | 
					
						
							|  |  |  |  |     return static_cast<double>(tv1.tv_sec) * 1000.0 + static_cast<double>(tv1.tv_usec) / 1000.0; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  | void TestRunner::run() | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     for (auto& test_path : tests_to_run) | 
					
						
							|  |  |  |  |         print_file_result(run_file_test(test_path)); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     print_test_results(); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 13:45:20 -07:00
										 |  |  |  | Result<NonnullRefPtr<JS::Program>, ParserError> parse_file(const String& file_path) | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-07-04 13:45:20 -07:00
										 |  |  |  |     auto file = Core::File::construct(file_path); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |     auto result = file->open(Core::IODevice::ReadOnly); | 
					
						
							| 
									
										
										
										
											2020-07-04 10:09:48 -07:00
										 |  |  |  |     if (!result) { | 
					
						
							| 
									
										
										
										
											2020-07-04 13:45:20 -07:00
										 |  |  |  |         printf("Failed to open the following file: \"%s\"\n", file_path.characters()); | 
					
						
							| 
									
										
										
										
											2020-07-04 10:09:48 -07:00
										 |  |  |  |         exit(1); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto contents = file->read_all(); | 
					
						
							|  |  |  |  |     String test_file_string(reinterpret_cast<const char*>(contents.data()), contents.size()); | 
					
						
							|  |  |  |  |     file->close(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto parser = JS::Parser(JS::Lexer(test_file_string)); | 
					
						
							|  |  |  |  |     auto program = parser.parse_program(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (parser.has_errors()) { | 
					
						
							|  |  |  |  |         auto error = parser.errors()[0]; | 
					
						
							| 
									
										
										
										
											2020-07-04 13:45:20 -07:00
										 |  |  |  |         return Result<NonnullRefPtr<JS::Program>, ParserError>(ParserError { error, error.source_location_hint(test_file_string) }); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 13:45:20 -07:00
										 |  |  |  |     return Result<NonnullRefPtr<JS::Program>, ParserError>(program); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  | Optional<JsonValue> get_test_results(JS::Interpreter& interpreter) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     auto result = interpreter.get_variable("__TestResults__", interpreter.global_object()); | 
					
						
							|  |  |  |  |     auto json_string = JS::JSONObject::stringify_impl(interpreter, interpreter.global_object(), result, JS::js_undefined(), JS::js_undefined()); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto json = JsonValue::from_string(json_string); | 
					
						
							|  |  |  |  |     if (!json.has_value()) | 
					
						
							|  |  |  |  |         return {}; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return json.value(); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | JSFileResult TestRunner::run_file_test(const String& test_path) | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  |     double start_time = get_time(); | 
					
						
							| 
									
										
										
										
											2020-07-05 10:47:40 -07:00
										 |  |  |  |     auto interpreter = JS::Interpreter::create<TestRunnerGlobalObject>(); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 13:45:20 -07:00
										 |  |  |  |     if (!m_test_program) { | 
					
						
							|  |  |  |  |         auto result = parse_file(String::format("%s/test-common.js", m_test_root.characters())); | 
					
						
							|  |  |  |  |         if (result.is_error()) { | 
					
						
							|  |  |  |  |             printf("Unable to parse test-common.js"); | 
					
						
							|  |  |  |  |             exit(1); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |         m_test_program = result.value(); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 13:45:20 -07:00
										 |  |  |  |     interpreter->run(interpreter->global_object(), *m_test_program); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto file_program = parse_file(String::format("%s/%s", m_test_root.characters(), test_path.characters())); | 
					
						
							|  |  |  |  |     if (file_program.is_error()) | 
					
						
							|  |  |  |  |         return { test_path, file_program.error() }; | 
					
						
							|  |  |  |  |     interpreter->run(interpreter->global_object(), *file_program.value()); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     auto test_json = get_test_results(*interpreter); | 
					
						
							|  |  |  |  |     if (!test_json.has_value()) { | 
					
						
							|  |  |  |  |         printf("Received malformed JSON from test \"%s\"\n", test_path.characters()); | 
					
						
							|  |  |  |  |         exit(1); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     JSFileResult file_result { test_path }; | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 20:23:46 -07:00
										 |  |  |  |     // Collect logged messages
 | 
					
						
							|  |  |  |  |     auto& arr = interpreter->get_variable("__UserOutput__", interpreter->global_object()).as_array(); | 
					
						
							|  |  |  |  |     for (auto& entry : arr.indexed_properties()) { | 
					
						
							|  |  |  |  |         auto message = entry.value_and_attributes(&interpreter->global_object()).value; | 
					
						
							|  |  |  |  |         file_result.logged_messages.append(message.to_string_without_side_effects()); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     test_json.value().as_object().for_each_member([&](const String& suite_name, const JsonValue& suite_value) { | 
					
						
							|  |  |  |  |         JSSuite suite { suite_name }; | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |         if (!suite_value.is_object()) { | 
					
						
							|  |  |  |  |             printf("Test JSON has a suite which is not an object (\"%s\")\n", test_path.characters()); | 
					
						
							|  |  |  |  |             exit(1); | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |         suite_value.as_object().for_each_member([&](const String& test_name, const JsonValue& test_value) { | 
					
						
							|  |  |  |  |             JSTest test { test_name, TestResult::Fail }; | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |             ASSERT(test_value.is_object()); | 
					
						
							|  |  |  |  |             ASSERT(test_value.as_object().has("result")); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |             auto result = test_value.as_object().get("result"); | 
					
						
							|  |  |  |  |             ASSERT(result.is_string()); | 
					
						
							|  |  |  |  |             auto result_string = result.as_string(); | 
					
						
							|  |  |  |  |             if (result_string == "pass") { | 
					
						
							|  |  |  |  |                 test.result = TestResult::Pass; | 
					
						
							|  |  |  |  |                 m_counts.tests_passed++; | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |             } else if (result_string == "fail") { | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |                 test.result = TestResult::Fail; | 
					
						
							|  |  |  |  |                 m_counts.tests_failed++; | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |                 suite.most_severe_test_result = TestResult::Fail; | 
					
						
							|  |  |  |  |             } else { | 
					
						
							|  |  |  |  |                 test.result = TestResult::Skip; | 
					
						
							|  |  |  |  |                 if (suite.most_severe_test_result == TestResult::Pass) | 
					
						
							|  |  |  |  |                     suite.most_severe_test_result = TestResult::Skip; | 
					
						
							|  |  |  |  |                 m_counts.tests_skipped++; | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             suite.tests.append(test); | 
					
						
							|  |  |  |  |         }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |         if (suite.most_severe_test_result == TestResult::Fail) { | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |             m_counts.suites_failed++; | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |             file_result.most_severe_test_result = TestResult::Fail; | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |             if (suite.most_severe_test_result == TestResult::Skip && file_result.most_severe_test_result == TestResult::Pass) | 
					
						
							|  |  |  |  |                 file_result.most_severe_test_result = TestResult::Skip; | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |             m_counts.suites_passed++; | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |         file_result.suites.append(suite); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     m_counts.files_total++; | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  |     file_result.time_taken = get_time() - start_time; | 
					
						
							|  |  |  |  |     m_total_elapsed_time_in_ms += file_result.time_taken; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     return file_result; | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | enum Modifier { | 
					
						
							|  |  |  |  |     BG_RED, | 
					
						
							|  |  |  |  |     BG_GREEN, | 
					
						
							|  |  |  |  |     FG_RED, | 
					
						
							|  |  |  |  |     FG_GREEN, | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |     FG_ORANGE, | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |     FG_GRAY, | 
					
						
							|  |  |  |  |     FG_BLACK, | 
					
						
							|  |  |  |  |     FG_BOLD, | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  |     ITALIC, | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |     CLEAR, | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | void print_modifiers(Vector<Modifier> modifiers) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     for (auto& modifier : modifiers) { | 
					
						
							|  |  |  |  |         auto code = [&]() -> String { | 
					
						
							|  |  |  |  |             switch (modifier) { | 
					
						
							|  |  |  |  |             case BG_RED: | 
					
						
							|  |  |  |  |                 return "\033[48;2;255;0;102m"; | 
					
						
							|  |  |  |  |             case BG_GREEN: | 
					
						
							|  |  |  |  |                 return "\033[48;2;102;255;0m"; | 
					
						
							|  |  |  |  |             case FG_RED: | 
					
						
							|  |  |  |  |                 return "\033[38;2;255;0;102m"; | 
					
						
							|  |  |  |  |             case FG_GREEN: | 
					
						
							|  |  |  |  |                 return "\033[38;2;102;255;0m"; | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |             case FG_ORANGE: | 
					
						
							|  |  |  |  |                 return "\033[38;2;255;102;0m"; | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |             case FG_GRAY: | 
					
						
							|  |  |  |  |                 return "\033[38;2;135;139;148m"; | 
					
						
							|  |  |  |  |             case FG_BLACK: | 
					
						
							|  |  |  |  |                 return "\033[30m"; | 
					
						
							|  |  |  |  |             case FG_BOLD: | 
					
						
							|  |  |  |  |                 return "\033[1m"; | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  |             case ITALIC: | 
					
						
							|  |  |  |  |                 return "\033[3m"; | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |             case CLEAR: | 
					
						
							|  |  |  |  |                 return "\033[0m"; | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |             ASSERT_NOT_REACHED(); | 
					
						
							|  |  |  |  |         }; | 
					
						
							|  |  |  |  |         printf("%s", code().characters()); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  | void TestRunner::print_file_result(const JSFileResult& file_result) const | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (file_result.most_severe_test_result == TestResult::Fail || file_result.error.has_value()) { | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |         print_modifiers({ BG_RED, FG_BLACK, FG_BOLD }); | 
					
						
							|  |  |  |  |         printf(" FAIL "); | 
					
						
							|  |  |  |  |         print_modifiers({ CLEAR }); | 
					
						
							|  |  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2020-07-05 07:50:08 -07:00
										 |  |  |  |         if (m_print_times || file_result.most_severe_test_result != TestResult::Pass) { | 
					
						
							|  |  |  |  |             print_modifiers({ BG_GREEN, FG_BLACK, FG_BOLD }); | 
					
						
							|  |  |  |  |             printf(" PASS "); | 
					
						
							|  |  |  |  |             print_modifiers({ CLEAR }); | 
					
						
							|  |  |  |  |         } else { | 
					
						
							|  |  |  |  |             return; | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  |     printf(" %s", file_result.name.characters()); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (m_print_times) { | 
					
						
							|  |  |  |  |         print_modifiers({ CLEAR, ITALIC, FG_GRAY }); | 
					
						
							|  |  |  |  |         if (file_result.time_taken < 1000) { | 
					
						
							|  |  |  |  |             printf(" (%dms)\n", static_cast<int>(file_result.time_taken)); | 
					
						
							|  |  |  |  |         } else { | 
					
						
							|  |  |  |  |             printf(" (%.3fs)\n", file_result.time_taken / 1000.0); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |         print_modifiers({ CLEAR }); | 
					
						
							|  |  |  |  |     } else { | 
					
						
							|  |  |  |  |         printf("\n"); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 20:23:46 -07:00
										 |  |  |  |     if (!file_result.logged_messages.is_empty()) { | 
					
						
							|  |  |  |  |         print_modifiers({ FG_GRAY, FG_BOLD }); | 
					
						
							|  |  |  |  | #ifdef __serenity__
 | 
					
						
							|  |  |  |  |         printf("     ℹ Console output:\n"); | 
					
						
							|  |  |  |  | #else
 | 
					
						
							|  |  |  |  |         // This emoji has a second invisible byte after it. The one above does not
 | 
					
						
							|  |  |  |  |         printf("    ℹ️  Console output:\n"); | 
					
						
							|  |  |  |  | #endif
 | 
					
						
							|  |  |  |  |         print_modifiers({ CLEAR, FG_GRAY }); | 
					
						
							|  |  |  |  |         for (auto& message : file_result.logged_messages) | 
					
						
							|  |  |  |  |             printf("         %s\n", message.characters()); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     if (file_result.error.has_value()) { | 
					
						
							|  |  |  |  |         auto test_error = file_result.error.value(); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |         print_modifiers({ FG_RED }); | 
					
						
							| 
									
										
										
										
											2020-07-04 20:23:46 -07:00
										 |  |  |  | #ifdef __serenity__
 | 
					
						
							|  |  |  |  |         printf("     ❌ The file failed to parse\n\n"); | 
					
						
							|  |  |  |  | #else
 | 
					
						
							|  |  |  |  |         // No invisible byte here, but the spacing still needs to be altered on the host
 | 
					
						
							|  |  |  |  |         printf("    ❌ The file failed to parse\n\n"); | 
					
						
							|  |  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |         print_modifiers({ FG_GRAY }); | 
					
						
							|  |  |  |  |         for (auto& message : test_error.hint.split('\n', true)) { | 
					
						
							| 
									
										
										
										
											2020-07-04 20:23:46 -07:00
										 |  |  |  |             printf("         %s\n", message.characters()); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |         } | 
					
						
							|  |  |  |  |         print_modifiers({ FG_RED }); | 
					
						
							| 
									
										
										
										
											2020-07-04 20:23:46 -07:00
										 |  |  |  |         printf("         %s\n\n", test_error.error.to_string().characters()); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |         return; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |     if (file_result.most_severe_test_result != TestResult::Pass) { | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |         for (auto& suite : file_result.suites) { | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |             if (suite.most_severe_test_result == TestResult::Pass) | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |                 continue; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |             bool failed = suite.most_severe_test_result == TestResult::Fail; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |             print_modifiers({ FG_GRAY, FG_BOLD }); | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |             if (failed) { | 
					
						
							| 
									
										
										
										
											2020-07-04 20:23:46 -07:00
										 |  |  |  | #ifdef __serenity__
 | 
					
						
							|  |  |  |  |                 printf("     ❌ Suite:  "); | 
					
						
							|  |  |  |  | #else
 | 
					
						
							|  |  |  |  |                 // No invisible byte here, but the spacing still needs to be altered on the host
 | 
					
						
							|  |  |  |  |                 printf("    ❌ Suite:  "); | 
					
						
							|  |  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2020-07-04 20:23:46 -07:00
										 |  |  |  | #ifdef __serenity__
 | 
					
						
							|  |  |  |  |                 printf("     ⚠ Suite:  "); | 
					
						
							|  |  |  |  | #else
 | 
					
						
							|  |  |  |  |                 // This emoji has a second invisible byte after it. The one above does not
 | 
					
						
							|  |  |  |  |                 printf("    ⚠️  Suite:  "); | 
					
						
							|  |  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             print_modifiers({ CLEAR, FG_GRAY }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |             if (suite.name == TOP_LEVEL_TEST_NAME) { | 
					
						
							|  |  |  |  |                 printf("<top-level>\n"); | 
					
						
							|  |  |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |                 printf("%s\n", suite.name.characters()); | 
					
						
							|  |  |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |             print_modifiers({ CLEAR }); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |             for (auto& test : suite.tests) { | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |                 if (test.result == TestResult::Pass) | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |                     continue; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |                 print_modifiers({ FG_GRAY, FG_BOLD }); | 
					
						
							| 
									
										
										
										
											2020-07-04 20:23:46 -07:00
										 |  |  |  |                 printf("         Test:   "); | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |                 if (test.result == TestResult::Fail) { | 
					
						
							|  |  |  |  |                     print_modifiers({ CLEAR, FG_RED }); | 
					
						
							|  |  |  |  |                     printf("%s (failed)\n", test.name.characters()); | 
					
						
							|  |  |  |  |                 } else { | 
					
						
							|  |  |  |  |                     print_modifiers({ CLEAR, FG_ORANGE }); | 
					
						
							|  |  |  |  |                     printf("%s (skipped)\n", test.name.characters()); | 
					
						
							|  |  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |                 print_modifiers({ CLEAR }); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |             } | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  | void TestRunner::print_test_results() const | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     printf("\nTest Suites: "); | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     if (m_counts.suites_failed) { | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |         print_modifiers({ FG_RED }); | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |         printf("%d failed, ", m_counts.suites_failed); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |         print_modifiers({ CLEAR }); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     if (m_counts.suites_passed) { | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |         print_modifiers({ FG_GREEN }); | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |         printf("%d passed, ", m_counts.suites_passed); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |         print_modifiers({ CLEAR }); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     printf("%d total\n", m_counts.suites_failed + m_counts.suites_passed); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     printf("Tests:       "); | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     if (m_counts.tests_failed) { | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |         print_modifiers({ FG_RED }); | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |         printf("%d failed, ", m_counts.tests_failed); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |         print_modifiers({ CLEAR }); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-07-04 12:57:12 -07:00
										 |  |  |  |     if (m_counts.tests_skipped) { | 
					
						
							|  |  |  |  |         print_modifiers({ FG_ORANGE }); | 
					
						
							|  |  |  |  |         printf("%d skipped, ", m_counts.tests_skipped); | 
					
						
							|  |  |  |  |         print_modifiers({ CLEAR }); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     if (m_counts.tests_passed) { | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |         print_modifiers({ FG_GREEN }); | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |         printf("%d passed, ", m_counts.tests_passed); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |         print_modifiers({ CLEAR }); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     printf("%d total\n", m_counts.tests_failed + m_counts.tests_passed); | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:37:50 -07:00
										 |  |  |  |     printf("Files:       %d total\n", m_counts.files_total); | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     printf("Time:        "); | 
					
						
							|  |  |  |  |     if (m_total_elapsed_time_in_ms < 1000.0) { | 
					
						
							|  |  |  |  |         printf("%dms\n\n", static_cast<int>(m_total_elapsed_time_in_ms)); | 
					
						
							|  |  |  |  |     } else { | 
					
						
							|  |  |  |  |         printf("%-.3fs\n\n", m_total_elapsed_time_in_ms / 1000.0); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  | int main(int argc, char** argv) | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  |     bool print_times = false; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     Core::ArgsParser args_parser; | 
					
						
							|  |  |  |  |     args_parser.add_option(print_times, "Show duration of each test", "show-time", 't'); | 
					
						
							|  |  |  |  |     args_parser.parse(argc, argv); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 11:50:24 -07:00
										 |  |  |  | #ifdef __serenity__
 | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  |     TestRunner("/home/anon/js-tests", print_times).run(); | 
					
						
							| 
									
										
										
										
											2020-07-04 11:50:24 -07:00
										 |  |  |  | #else
 | 
					
						
							|  |  |  |  |     char* serenity_root = getenv("SERENITY_ROOT"); | 
					
						
							|  |  |  |  |     if (!serenity_root) { | 
					
						
							|  |  |  |  |         printf("test-js requires the SERENITY_ROOT environment variable to be set"); | 
					
						
							|  |  |  |  |         return 1; | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-07-04 13:23:38 -07:00
										 |  |  |  |     TestRunner(String::format("%s/Libraries/LibJS/Tests", serenity_root), print_times).run(); | 
					
						
							| 
									
										
										
										
											2020-07-04 11:50:24 -07:00
										 |  |  |  | #endif
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-03 14:36:58 -07:00
										 |  |  |  |     return 0; | 
					
						
							|  |  |  |  | } |