From 5b736855220a97a974718f6b37874d144b0488b5 Mon Sep 17 00:00:00 2001 From: Sam Surtees Date: Mon, 30 Apr 2018 23:41:45 +1000 Subject: [PATCH] Added GoogleTest style output for unit tests --- modules/self-test/self-test.lua | 7 +- modules/self-test/test_declare.lua | 7 +- modules/self-test/test_runner.lua | 123 ++++++++++++++++++++--------- 3 files changed, 92 insertions(+), 45 deletions(-) diff --git a/modules/self-test/self-test.lua b/modules/self-test/self-test.lua index 3a15d271..deca830d 100644 --- a/modules/self-test/self-test.lua +++ b/modules/self-test/self-test.lua @@ -46,15 +46,10 @@ error(err, 0) end - local startTime = os.clock() - local passed, failed = m.runTest(test) - local elapsed = os.clock() - startTime - - printf("%d tests passed, %d failed in %0.02f seconds", passed, failed, elapsed) - if failed > 0 then + printf("\n %d FAILED TEST%s", failed, iif(failed > 1, "S", "")) os.exit(5) end end diff --git a/modules/self-test/test_declare.lua b/modules/self-test/test_declare.lua index 9eae15aa..a1f51161 100644 --- a/modules/self-test/test_declare.lua +++ b/modules/self-test/test_declare.lua @@ -40,7 +40,12 @@ -- Setup a metatable for the test suites to use, this will catch duplicate tests local suite = setmetatable({}, { __index = _suite, - __newindex = function (table, key, value) if m.detectDuplicateTests and _suite[key] ~= nil then error('Duplicate test "'.. key .. '"', 2) end _suite[key] = value end, + __newindex = function (table, key, value) + if m.detectDuplicateTests and _suite[key] ~= nil then + error('Duplicate test "'.. key .. '"', 2) + end + _suite[key] = value + end, __pairs = function (table) return pairs(_suite) end, __ipairs = function (table) return ipairs(_suite) end, }) diff --git a/modules/self-test/test_runner.lua b/modules/self-test/test_runner.lua index c9ae1461..068a3e1d 100644 --- a/modules/self-test/test_runner.lua +++ b/modules/self-test/test_runner.lua @@ -16,68 +16,75 @@ function m.runTest(test) - local scopedTestCall - - if test.testFunction then - scopedTestCall = _.runTest - elseif test.suite then - scopedTestCall = _.runTestSuite - else - scopedTestCall = _.runAllTests - end - - return scopedTestCall(test) - end - - - - function _.runAllTests() - local passed = 0 local failed = 0 + local failedTests = {} local suites = m.getSuites() - for suiteName, suite in pairs(suites) do + local suitesKeys, suiteTestsKeys, totalTestCount = _.preprocessTests(suites, test) + + _.log(term.lightGreen, "[==========]", string.format(" Running %d tests from %d test suites.", totalTestCount, #suitesKeys)) + local startTime = os.clock() + + for index, suiteName in ipairs(suitesKeys) do + suite = suites[suiteName] if not m.isSuppressed(suiteName) then - local test = {} + local test = { + suiteName = suiteName, + suite = suite + } - test.suiteName = suiteName - test.suite = suite + local suiteFailed, suiteFailedTests = _.runTestSuite(test, suiteTestsKeys[suiteName]) - local suitePassed, suiteFailed = _.runTestSuite(test) - - passed = passed + suitePassed failed = failed + suiteFailed + failedTests = table.join(failedTests, suiteFailedTests) end end - return passed, failed + _.log(term.lightGreen, "[==========]", string.format(" %d tests from %d test suites ran. (%.0f ms total)", totalTestCount, #suitesKeys, (os.clock() - startTime) * 1000)) + _.log(term.lightGreen, "[ PASSED ]", string.format(" %d tests.", totalTestCount - failed)) + if failed > 0 then + _.log(term.lightRed, "[ FAILED ]", string.format(" %d tests, listed below:", failed)) + for index, testName in ipairs(failedTests) do + _.log(term.lightRed, "[ FAILED ]", " " .. testName) + end + end + + return (totalTestCount - failed), failed end - function _.runTestSuite(test) - local passed = 0 + function _.runTestSuite(test, keys) local failed = 0 + local failedTests = {} + _.log(term.lightGreen, "[----------]", string.format(" %d tests from %s", #keys, test.suiteName)) + local startTime = os.clock() if test.suite ~= nil then - for testName, testFunction in pairs(test.suite) do + for index, testName in ipairs(keys) do + testFunction = test.suite[testName] test.testName = testName test.testFunction = testFunction if m.isValid(test) and not m.isSuppressed(test.suiteName .. "." .. test.testName) then - local np, nf = _.runTest(test) - passed = passed + np - failed = failed + nf + local err = _.runTest(test) + if err then + failed = failed + 1 + table.insert(failedTests, test.suiteName .. "." .. test.testName .. "\n" .. err) + end end end end - return passed, failed + _.log(term.lightGreen, "[----------]", string.format(" %d tests from %s (%.0f ms total)\n", #keys, test.suiteName, (os.clock() - startTime) * 1000)) + return failed, failedTests end function _.runTest(test) + _.log(term.lightGreen, "[ RUN ]", string.format(" %s.%s", test.suiteName, test.testName)) + local startTime = os.clock() local cwd = os.getcwd() local hooks = _.installTestingHooks() @@ -101,18 +108,58 @@ os.chdir(cwd) if ok then - return 1, 0 + _.log(term.lightGreen, "[ OK ]", string.format(" %s.%s (%.0f ms)", test.suiteName, test.testName, (os.clock() - startTime) * 1000)) + return nil else - term.pushColor(term.warningColor) - io.write(string.format("%s.%s", test.suiteName, test.testName)) - term.popColor() - m.print(string.format(": %s", err)) - return 0, 1 + _.log(term.lightRed, "[ FAILED ]", string.format(" %s.%s (%.0f ms)", test.suiteName, test.testName, (os.clock() - startTime) * 1000)) + m.print(string.format("%s", err)) + return err end end + function _.log(color, left, right) + term.pushColor(color) + io.write(left) + term.popColor() + m.print(right) + end + + + + function _.preprocessTests(suites, filter) + local suitesKeys = {} + local suiteTestsKeys = {} + local totalTestCount = 0 + + for suiteName, suite in pairs(suites) do + if not m.isSuppressed(suiteName) and suite ~= nil and (not filter.suiteName or filter.suiteName == suiteName) then + local test = {} + + table.insertsorted(suitesKeys, suiteName) + + test.suiteName = suiteName + test.suite = suite + + suiteTestsKeys[suiteName] = {} + for testName, testFunction in pairs(suite) do + test.testName = testName + test.testFunction = testFunction + + if m.isValid(test) and not m.isSuppressed(test.suiteName .. "." .. test.testName) and (not filter.testName or filter.testName == testName) then + table.insertsorted(suiteTestsKeys[suiteName], testName) + end + end + totalTestCount = totalTestCount + #suiteTestsKeys[suiteName] + end + end + + return suitesKeys, suiteTestsKeys, totalTestCount + end + + + function _.installTestingHooks() local hooks = {}