luau/tools/test_dcr.py
vegorov-rbx 4b267aa5c5
Sync to upstream/release/574 (#910)
* Added a limit on how many instructions the Compiler can safely produce
(reported by @TheGreatSageEqualToHeaven)

C++ API Changes:
* With work started on read-only and write-only properties,
`Property::type` member variable has been replaced with `TypeId type()`
and `setType(TypeId)` functions.
* New `LazyType` unwrap callback now has a `void` return type, all
that's required from the callback is to write into `unwrapped` field.

In our work on the new type solver, the following issues were fixed:

* Work has started to support https://github.com/Roblox/luau/pull/77 and
https://github.com/Roblox/luau/pull/79
* Refinements are no longer applied on l-values, removing some
false-positive errors
* Improved overload resolution against expected result type
* `Frontend::prepareModuleScope` now works in the new solver
* Cofinite strings are now comparable

And these are the changes in native code generation (JIT):

* Fixed MIN_NUM and MAX_NUM constant fold when one of the arguments is
NaN
* Added constant folding for number conversions and bit operations
* Value spilling and rematerialization is now supported on arm64
* Improved FASTCALL2K IR generation to support second argument constant
* Added value numbering and load/store propagation optimizations
* Added STORE_VECTOR on arm64, completing the IR lowering on this target
2023-04-28 12:55:13 -07:00

240 lines
6.7 KiB
Python

#!/usr/bin/python3
# This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
import argparse
import os.path
import subprocess as sp
import sys
import xml.sax as x
import colorama as c
c.init()
SCRIPT_PATH = os.path.split(sys.argv[0])[0]
FAIL_LIST_PATH = os.path.join(SCRIPT_PATH, "faillist.txt")
def loadFailList():
with open(FAIL_LIST_PATH) as f:
return set(map(str.strip, f.readlines()))
def safeParseInt(i, default=0):
try:
return int(i)
except ValueError:
return default
def makeDottedName(path):
return ".".join(path)
class Handler(x.ContentHandler):
def __init__(self, failList):
self.currentTest = []
self.failList = failList # Set of dotted test names that are expected to fail
self.results = {} # {DottedName: TrueIfTheTestPassed}
self.numSkippedTests = 0
self.pass_count = 0
self.fail_count = 0
self.test_count = 0
self.crashed_tests = []
def startElement(self, name, attrs):
if name == "TestSuite":
self.currentTest.append(attrs["name"])
elif name == "TestCase":
self.currentTest.append(attrs["name"])
elif name == "OverallResultsAsserts":
if self.currentTest:
passed = attrs["test_case_success"] == "true"
dottedName = makeDottedName(self.currentTest)
# Sometimes we get multiple XML trees for the same test. All of
# them must report a pass in order for us to consider the test
# to have passed.
r = self.results.get(dottedName, True)
self.results[dottedName] = r and passed
self.test_count += 1
if passed:
self.pass_count += 1
else:
self.fail_count += 1
elif name == "OverallResultsTestCases":
self.numSkippedTests = safeParseInt(attrs.get("skipped", 0))
elif name == "Exception":
if attrs.get("crash") == "true":
self.crashed_tests.append(makeDottedName(self.currentTest))
def endElement(self, name):
if name == "TestCase":
self.currentTest.pop()
elif name == "TestSuite":
self.currentTest.pop()
def print_stderr(*args, **kw):
print(*args, **kw, file=sys.stderr)
def main():
parser = argparse.ArgumentParser(
description="Run Luau.UnitTest with deferred constraint resolution enabled"
)
parser.add_argument(
"path", action="store", help="Path to the Luau.UnitTest executable"
)
parser.add_argument(
"--dump",
dest="dump",
action="store_true",
help="Instead of doing any processing, dump the raw output of the test run. Useful for debugging this tool.",
)
parser.add_argument(
"--write",
dest="write",
action="store_true",
help="Write a new faillist.txt after running tests.",
)
parser.add_argument(
"--rwp",
dest="rwp",
action="store_true",
help="Run the tests with read-write properties enabled.",
)
parser.add_argument("--randomize", action="store_true", help="Pick a random seed")
parser.add_argument(
"--random-seed",
action="store",
dest="random_seed",
type=int,
help="Accept a specific RNG seed",
)
args = parser.parse_args()
if args.write and args.rwp:
print_stderr(
"Cannot run test_dcr.py with --write *and* --rwp. You don't want to commit local type inference faillist.txt yet."
)
sys.exit(1)
failList = loadFailList()
flags = ["true", "DebugLuauDeferredConstraintResolution"]
if args.rwp:
flags.append("DebugLuauReadWriteProperties")
commandLine = [args.path, "--reporters=xml", "--fflags=" + ",".join(flags)]
if args.random_seed:
commandLine.append("--random-seed=" + str(args.random_seed))
elif args.randomize:
commandLine.append("--randomize")
print_stderr(">", " ".join(commandLine))
p = sp.Popen(
commandLine,
stdout=sp.PIPE,
)
handler = Handler(failList)
if args.dump:
for line in p.stdout:
sys.stdout.buffer.write(line)
return
else:
try:
x.parse(p.stdout, handler)
except x.SAXParseException as e:
print_stderr(
f"XML parsing failed during test {makeDottedName(handler.currentTest)}. That probably means that the test crashed"
)
sys.exit(1)
p.wait()
unexpected_fails = 0
unexpected_passes = 0
for testName, passed in handler.results.items():
if passed and testName in failList:
unexpected_passes += 1
print_stderr(
f"UNEXPECTED: {c.Fore.RED}{testName}{c.Fore.RESET} should have failed"
)
elif not passed and testName not in failList:
unexpected_fails += 1
print_stderr(
f"UNEXPECTED: {c.Fore.GREEN}{testName}{c.Fore.RESET} should have passed"
)
if unexpected_fails or unexpected_passes:
print_stderr("")
print_stderr(f"Unexpected fails: {unexpected_fails}")
print_stderr(f"Unexpected passes: {unexpected_passes}")
pass_percent = int(handler.pass_count / handler.test_count * 100)
print_stderr("")
print_stderr(
f"{handler.pass_count} of {handler.test_count} tests passed. ({pass_percent}%)"
)
print_stderr(f"{handler.fail_count} tests failed.")
if args.write:
newFailList = sorted(
(
dottedName
for dottedName, passed in handler.results.items()
if not passed
),
key=str.lower,
)
with open(FAIL_LIST_PATH, "w", newline="\n") as f:
for name in newFailList:
print(name, file=f)
print_stderr("Updated faillist.txt")
if handler.crashed_tests:
print_stderr()
for test in handler.crashed_tests:
print_stderr(
f"{c.Fore.RED}{test}{c.Fore.RESET} threw an exception and crashed the test process!"
)
if handler.numSkippedTests > 0:
print_stderr(f"{handler.numSkippedTests} test(s) were skipped!")
ok = (
not handler.crashed_tests
and handler.numSkippedTests == 0
and all(
not passed == (dottedName in failList)
for dottedName, passed in handler.results.items()
)
)
if ok:
print_stderr("Everything in order!")
sys.exit(0 if ok else 1)
if __name__ == "__main__":
main()