Skip to content

Commit 8e097dd

Browse files
committed
Re-write test runner
Add code coverage check
1 parent 091781f commit 8e097dd

File tree

5 files changed

+141
-9
lines changed

5 files changed

+141
-9
lines changed

.github/workflows/run-tests.yml

+32-8
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
id: lib-version
1818
run: |
1919
curl "https://github.com/SmartThingsCommunity/SmartThingsEdgeDrivers/releases/latest" -s -L -I -o /dev/null -w '%{url_effective}' > test.log
20-
echo "::set-output name=url::$(cat test.log)"
20+
echo "url=$(cat test.log)" >> $GITHUB_OUTPUT
2121
- name: Try to retrieve cache
2222
id: cached-libs
2323
uses: actions/cache@v3
@@ -82,8 +82,6 @@ jobs:
8282

8383
run-driver-tests:
8484
runs-on: ubuntu-latest
85-
env:
86-
LUA_PATH: /home/runner/work/lua_libs/?.lua;./?.lua;/home/runner/work/lua_libs/?/init.lua;./?/init.lua
8785
needs:
8886
[ get-latest-release-artifact, get-dev-artifact ]
8987
if: ${{ always() && contains(needs.*.result, 'success') && !contains(needs.*.result, 'failure') }}
@@ -106,17 +104,43 @@ jobs:
106104
- name: Install lua
107105
run: |
108106
sudo apt-get update
109-
sudo apt-get install lua5.3
107+
sudo apt-get install lua5.3 liblua5.3-dev luarocks
108+
- name: Install lua rocks
109+
run: |
110+
wget https://luarocks.org/manifests/hisham/luacov-0.15.0-1.rockspec
111+
wget https://luarocks.org/manifests/ssharma/luacov-cobertura-0.2-1.rockspec
112+
sudo luarocks install luacov-0.15.0-1.rockspec
113+
sudo luarocks install luacov-cobertura-0.2-1.rockspec
114+
- name: Set LUA_PATH
115+
id: lua_path
116+
env:
117+
LUA_PATH_APPEND: /home/runner/work/lua_libs/?.lua;./?.lua;/home/runner/work/lua_libs/?/init.lua;./?/init.lua
118+
run: |
119+
eval "$(luarocks path --bin)"
120+
echo "lua_path=$LUA_PATH_APPEND;$LUA_PATH" >> $GITHUB_OUTPUT
110121
- uses: actions/checkout@v3
122+
- name: get changed drivers
123+
id: changed-drivers
124+
uses: tj-actions/changed-files@v35
125+
with:
126+
dir_names: true
127+
dir_names_max_depth: 3
128+
files: "drivers"
129+
- run: echo ${{ steps.changed-drivers.outputs.all_modified_files }}
111130
- name: Install Python requirements
112131
run: pip install -r tools/requirements.txt
113-
- name: Prepare test output directory
114-
run: mkdir test-output
115132
- name: Run the tests
116133
id: run-tests
117-
run: python tools/run_driver_tests.py -j test-output/tests.xml
134+
run: python tools/run_driver_tests_p.py ${{ steps.changed-drivers.outputs.all_modified_files }}
135+
env:
136+
LUA_PATH: ${{ steps.lua_path.outputs.lua_path }}
137+
- name: Check code coverage
138+
uses: 5monkeys/cobertura-action@master
139+
with:
140+
path: tools/coverage_output/*_coverage.xml
141+
minimum_coverage: 90
118142
- name: Publish Test Results
119143
if: always()
120144
uses: EnricoMi/publish-unit-test-result-action@v2
121145
with:
122-
junit_files: "test-output/tests.xml"
146+
junit_files: "tools/test_output/*.xml"

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@
33
.vscode/
44
docs/
55
lua_libs-api_*
6+
*.out
7+
tools/test_output/*
8+
tools/coverage_output/*
9+
.DS_Store

tools/config.luacov

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
configuration = {
2+
exclude = {
3+
"scripting-engine",
4+
"lua_libs",
5+
"test",
6+
"unofficial"
7+
},
8+
cobertura = {
9+
filenameparser = function(filename)
10+
local cwd = os.getenv("PWD")
11+
return cwd..'/'..filename
12+
end
13+
}
14+
}
15+
16+
return configuration

tools/requirements.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
junit_xml
22
requests
3-
PyYAML==5.4.1
3+
PyYAML==5.4.1
4+
regex

tools/run_driver_tests_p.py

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/usr/bin/env python3
2+
3+
import subprocess, junit_xml, os, sys
4+
from pathlib import Path
5+
from multiprocessing import Pool
6+
import regex as re # supports multi-threading
7+
8+
test_case_re = re.compile("Running test \"(.*?)\".+?-{2,}.*?(PASSED|FAILED)", flags=re.DOTALL)
9+
LUACOV_CONFIG = Path(os.path.abspath(__file__)).parent.joinpath("config.luacov")
10+
DRIVER_DIRS = Path(os.path.abspath(__file__)).parents[1].joinpath("drivers")
11+
DRIVERS = [driver for driver in DRIVER_DIRS.glob("*/*") if driver.is_dir()] # this gets all the children of the children of the drivers directory
12+
CHANGED_DRIVERS = [Path(driver).name for driver in sys.argv[1:]]
13+
14+
def per_driver_task(driver_dir):
15+
os.chdir(driver_dir.joinpath('src'))
16+
results = map(run_test, driver_dir.glob("src/test/test_*.lua"))
17+
successes, failures, failure_output, test_suites = 0, 0, "", []
18+
for result in results:
19+
test_suites.append(result[0])
20+
successes += result[1]
21+
failures += result[2]
22+
if result[3] != "":
23+
failure_output += result[3] + '\n'
24+
with open(driver_dir.parent.parent.parent.joinpath("tools/test_output/").joinpath(driver_dir.name+"_test_output.xml"), 'w+') as outfile:
25+
junit_xml.to_xml_report_file(outfile, test_suites)
26+
print("{}: passed {} of {} tests".format(driver_dir.name, successes, successes+failures))
27+
if failure_output != "":
28+
failure_output = driver_dir.name + ": \n" + failure_output
29+
else:
30+
failure_output = None
31+
if driver_dir.name in CHANGED_DRIVERS:
32+
with driver_dir.parent.parent.parent.joinpath("tools/coverage_output").joinpath(driver_dir.name+"_coverage.xml") as outfile:
33+
subprocess.run("luacov-cobertura -o {} -c {}".format(outfile, LUACOV_CONFIG), shell=True)
34+
return failure_output
35+
36+
def run_test(test_file):
37+
if test_file.parent.parent.parent.name in CHANGED_DRIVERS:
38+
a = subprocess.run("lua -lluacov {}".format(test_file), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
39+
else:
40+
a = subprocess.run("lua {}".format(test_file), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
41+
error = a.stderr.decode()
42+
if error and error != "":
43+
print(error)
44+
parsed_output = a.stdout.decode()
45+
test_suite_name = str(test_file)[str(test_file).rindex('/')+1:-4].replace('_',' ')
46+
test_suite = junit_xml.TestSuite(test_suite_name)
47+
successes, failures, failure_output, test_cases = 0, 0, "", []
48+
for match in test_case_re.finditer(parsed_output):
49+
test_case = junit_xml.TestCase(match[1])
50+
test_case.stdout = match[0]
51+
if match[2] == "FAILED":
52+
failures += 1
53+
if "traceback" in match[0]:
54+
failure_output += "\t{} ERROR in {}\n".format(test_suite_name, match[1])
55+
test_case.add_error_info("ERROR", match[0])
56+
else:
57+
failure_output += "\t{} FAILED on {}\n".format(test_suite_name, match[1])
58+
test_case.add_failure_info("FAILED", match[0])
59+
else:
60+
successes += 1
61+
test_cases.append(test_case)
62+
test_suite.test_cases = test_cases
63+
return (test_suite, successes, failures, failure_output)
64+
65+
if __name__ == "__main__":
66+
67+
try:
68+
os.mkdir(Path(os.path.abspath(__file__)).parent.joinpath("test_output"))
69+
except FileExistsError:
70+
pass
71+
72+
try:
73+
os.mkdir(Path(os.path.abspath(__file__)).parent.joinpath("coverage_output"))
74+
except FileExistsError:
75+
pass
76+
77+
failure_output = ""
78+
with Pool() as pool:
79+
failure_output = pool.map(per_driver_task, DRIVERS)
80+
81+
exit_code = 0
82+
83+
for test_case in failure_output:
84+
if test_case:
85+
print(test_case)
86+
exit_code = 1
87+
sys.exit(exit_code)

0 commit comments

Comments
 (0)