Skip to content

Commit 15fb9f8

Browse files
committed
Enhance unit tests (cztomczak#59).
Display summary with real number of tests ran including sub-tests in main_test. Ensure that messages are coming from Javascript in OnConsoleMessage. Automate checking for True/False asserts in ClientHandler and External.
1 parent 1aabf8e commit 15fb9f8

File tree

3 files changed

+108
-75
lines changed

3 files changed

+108
-75
lines changed
Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""Run unit tests. With no arguments all tests are run. Read notes below.
44
55
Usage:
6-
_runner.py [FILE | _TESTCASE]
6+
_test_runner.py [FILE | _TESTCASE]
77
88
Options:
99
FILE Run tests from single file
@@ -83,7 +83,7 @@ def run_testcase(self, testcase):
8383
self._discover("[!_]*.py", testcase)
8484
assert not self._count_suites(self._isolated_suites)
8585
if not self._count_suites(self._suites):
86-
print("[_runner.py] ERROR: test case not found")
86+
print("[_test_runner.py] ERROR: test case not found")
8787
sys.exit(1)
8888
# Import errors found during discovery are ignored
8989
self._run_suites(self._suites)
@@ -140,14 +140,17 @@ def _run_suites_in_isolation(self, suites):
140140
# Run test using new instance of Python interpreter
141141
try:
142142
output = subprocess.check_output(
143-
["python", "_runner.py", testcase_id],
143+
["python", "_test_runner.py", testcase_id],
144144
stderr=subprocess.STDOUT)
145145
exit_code = 0
146146
except subprocess.CalledProcessError as exc:
147147
output = exc.output
148148
exit_code = exc.returncode
149149
if type(output) != str:
150150
output = output.decode("utf-8", errors="replace")
151+
match = re.search(r"Ran (\d+) sub-tests in \w+", output)
152+
if match:
153+
self.ran += int(match.group(1))
151154
sys.stdout.write(output)
152155
# If tests failed parse output for errors/failures
153156
if exit_code:
@@ -228,9 +231,9 @@ def _print_summary(self):
228231
# type: () -> None
229232
"""Print summary and exit."""
230233
print("-"*70)
231-
print("[_runner.py] Ran "+str(self.ran)+" tests in total")
234+
print("[_test_runner.py] Ran "+str(self.ran)+" tests in total")
232235
if self.errors or self.failures:
233-
failed_str = "[_runner.py] FAILED ("
236+
failed_str = "[_test_runner.py] FAILED ("
234237
if self.failures:
235238
failed_str += ("failures="+str(self.failures))
236239
if self.errors:
@@ -240,7 +243,7 @@ def _print_summary(self):
240243
failed_str += ")"
241244
print(failed_str)
242245
else:
243-
print("[_runner.py] OK")
246+
print("[_test_runner.py] OK")
244247
self._exit()
245248

246249
def _exit(self):

unittests/isolated_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import unittest
88
# noinspection PyUnresolvedReferences
9-
import _runner
9+
import _test_runner
1010
from os.path import basename
1111

1212
# Globals
@@ -35,4 +35,4 @@ def test_isolated3(self):
3535

3636

3737
if __name__ == "__main__":
38-
_runner.main(basename(__file__))
38+
_test_runner.main(basename(__file__))

unittests/main_test.py

Lines changed: 97 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
"""General testing of CEF Python."""
22

3-
# To show the window for an extended period of time increase this number.
4-
MESSAGE_LOOP_RANGE = 25 # each iteration is 0.01 sec
5-
63
import unittest
74
# noinspection PyUnresolvedReferences
8-
import _runner
5+
import _test_runner
96
from os.path import basename
107
from cefpython3 import cefpython as cef
118
import time
129
import base64
10+
import sys
11+
12+
# To show the window for an extended period of time increase this number.
13+
MESSAGE_LOOP_RANGE = 25 # each iteration is 0.01 sec
1314

1415
g_browser = None
1516
g_client_handler = None
@@ -27,36 +28,41 @@
2728
</style>
2829
<script>
2930
function print(msg) {
31+
console.log(msg+" [JS]");
3032
msg = msg.replace("ok", "<b style='color:green'>ok</b>");
3133
msg = msg.replace("error", "<b style='color:red'>error</b>");
3234
document.getElementById("console").innerHTML += msg+"<br>";
3335
}
3436
window.onload = function(){
3537
print("window.onload() ok");
3638
37-
print("test_property1 = <i>"+test_property1+"</i>")
39+
// Test binding property: test_property1
3840
if (test_property1 == "Test binding property to the 'window' object") {
39-
print("ok");
41+
print("test_property_1 ok");
4042
} else {
41-
print("error");
42-
throw "test_property1 contains invalid string";
43+
throw new Error("test_property1 contains invalid string");
4344
}
4445
45-
print("test_property2 = <i>"+JSON.stringify(test_property2)+"</i>");
46+
// Test binding property: test_property2
4647
if (JSON.stringify(test_property2) == '{"key1":"Test binding property'+
4748
' to the \\'window\\' object","key2":["Inside list",1,2]}') {
48-
print("ok");
49+
print("test_property2 ok");
4950
} else {
50-
print("error");
51-
throw "test_property2 contains invalid value";
51+
throw new Error("test_property2 contains invalid value");
5252
}
5353
54+
// Test binding function: test_function
5455
test_function();
5556
print("test_function() ok");
5657
58+
// Test binding external object and use of javascript<>python callbacks
5759
external.test_callbacks(function(msg_from_python, py_callback){
58-
print("test_callbacks(): "+msg_from_python+" ok")
59-
print("py_callback.toString()="+py_callback.toString());
60+
if (msg_from_python == "String sent from Python") {
61+
print("test_callbacks() ok");
62+
} else {
63+
throw new Error("test_callbacks(): msg_from_python contains"+
64+
" invalid value");
65+
}
6066
py_callback("String sent from Javascript");
6167
print("py_callback() ok");
6268
});
@@ -73,34 +79,48 @@
7379
g_datauri = "data:text/html;base64,"+base64.b64encode(g_datauri_data.encode(
7480
"utf-8", "replace")).decode("utf-8", "replace")
7581

82+
g_subtests_ran = 0
83+
84+
85+
def subtest_message(message):
86+
global g_subtests_ran
87+
g_subtests_ran += 1
88+
print(str(g_subtests_ran) + ". " + message)
89+
sys.stdout.flush()
90+
7691

7792
class MainTest_IsolatedTest(unittest.TestCase):
7893

7994
def test_main(self):
8095
"""Main entry point."""
8196
# All this code must run inside one single test, otherwise strange
8297
# things happen.
98+
print("")
8399

84100
# Test initialization of CEF
85101
cef.Initialize({
86102
"debug": False,
87103
"log_severity": cef.LOGSEVERITY_ERROR,
88104
"log_file": "",
89105
})
106+
subtest_message("cef.Initialize() ok")
90107

91108
# Test global client callback
92109
global g_client_handler
93110
g_client_handler = ClientHandler(self)
94111
cef.SetGlobalClientCallback("OnAfterCreated",
95112
g_client_handler._OnAfterCreated)
113+
subtest_message("cef.SetGlobalClientCallback() ok")
96114

97115
# Test creation of browser
98116
global g_browser
99117
g_browser = cef.CreateBrowserSync(url=g_datauri)
100118
self.assertIsNotNone(g_browser, "Browser object")
119+
subtest_message("cef.CreateBrowserSync() ok")
101120

102121
# Test client handler
103122
g_browser.SetClientHandler(g_client_handler)
123+
subtest_message("browser.SetClientHandler() ok")
104124

105125
# Test javascript bindings
106126
global g_external
@@ -112,70 +132,76 @@ def test_main(self):
112132
bindings.SetProperty("test_property2", g_external.test_property2)
113133
bindings.SetObject("external", g_external)
114134
g_browser.SetJavascriptBindings(bindings)
135+
subtest_message("browser.SetJavascriptBindings() ok")
115136

116137
# Run message loop for 0.5 sec.
117138
# noinspection PyTypeChecker
118139
for i in range(MESSAGE_LOOP_RANGE):
119140
cef.MessageLoopWork()
120141
time.sleep(0.01)
142+
subtest_message("cef.MessageLoopWork() ok")
121143

122144
# Test browser closing. Remember to clean reference.
123145
g_browser.CloseBrowser(True)
124146
g_browser = None
147+
subtest_message("browser.CloseBrowser() ok")
125148

126149
# Give it some time to close before calling shutdown.
127150
# noinspection PyTypeChecker
128151
for i in range(25):
129152
cef.MessageLoopWork()
130153
time.sleep(0.01)
131154

132-
# Client handler asserts
133-
self.assertTrue(g_client_handler.OnAfterCreated_called,
134-
"OnAfterCreated() call")
135-
self.assertTrue(g_client_handler.OnLoadStart_called,
136-
"OnLoadStart() call")
137-
self.assertTrue(g_client_handler.OnLoadEnd_called,
138-
"OnLoadEnd() call")
139-
self.assertTrue(g_client_handler.FrameSourceVisitor_called,
140-
"FrameSourceVisitor.Visit() call")
141-
self.assertEqual(g_client_handler.javascript_errors, 0,
142-
"Javascript errors caught in OnConsoleMessage")
143-
144-
# Javascript asserts
145-
self.assertTrue(g_external.test_function_called,
146-
"js test_function() call")
147-
self.assertTrue(g_external.test_callbacks_called,
148-
"js external.test_callbacks() call")
149-
self.assertTrue(g_external.py_callback_called,
150-
"py_callback() call from js external.test_callbacks()")
155+
# Client handler asserts and javascript External asserts
156+
for obj in [g_client_handler, g_external]:
157+
test_for_True = False # Test whether asserts are working correctly
158+
for key, value in obj.__dict__.items():
159+
if key == "test_for_True":
160+
test_for_True = True
161+
continue
162+
if "_True" in key:
163+
self.assertTrue(value, "Check assert: "+key)
164+
subtest_message(obj.__class__.__name__ + "." +
165+
key.replace("_True", "") +
166+
" ok")
167+
elif "_False" in key:
168+
self.assertFalse(value, "Check assert: "+key)
169+
subtest_message(obj.__class__.__name__ + "." +
170+
key.replace("_False", "") +
171+
" ok")
172+
self.assertTrue(test_for_True)
151173

152174
# Test shutdown of CEF
153175
cef.Shutdown()
176+
subtest_message("cef.Shutdown() ok")
154177

178+
# Display real number of tests there were run
179+
print("\nRan " + str(g_subtests_ran) + " sub-tests in test_main")
180+
sys.stdout.flush()
155181

156-
class ClientHandler:
157-
test_case = None
158-
159-
OnAfterCreated_called = False
160-
OnLoadStart_called = False
161-
OnLoadEnd_called = False
162-
163-
FrameSourceVisitor_called = False
164-
frame_source_visitor = None
165-
166-
javascript_errors = 0
167182

183+
class ClientHandler(object):
168184
def __init__(self, test_case):
169185
self.test_case = test_case
186+
self.frame_source_visitor = None
187+
188+
# Asserts for True/False will be checked just before shutdown
189+
self.test_for_True = True # Test whether asserts are working correctly
190+
self.OnAfterCreated_True = False
191+
self.OnLoadStart_True = False
192+
self.OnLoadEnd_True = False
193+
self.FrameSourceVisitor_True = False
194+
self.javascript_errors_False = False
195+
self.OnConsoleMessage_True = False
170196

171197
# noinspection PyUnusedLocal
172198
def _OnAfterCreated(self, browser):
173-
self.OnAfterCreated_called = True
199+
self.OnAfterCreated_True = True
174200

175201
# noinspection PyUnusedLocal
176202
def OnLoadStart(self, browser, frame):
177203
self.test_case.assertEqual(browser.GetUrl(), g_datauri)
178-
self.OnLoadStart_called = True
204+
self.OnLoadStart_True = True
179205

180206
# noinspection PyUnusedLocal
181207
def OnLoadEnd(self, browser, frame, http_code):
@@ -184,18 +210,21 @@ def OnLoadEnd(self, browser, frame, http_code):
184210
frame.GetSource(self.frame_source_visitor)
185211
browser.ExecuteJavascript(
186212
"print('ClientHandler.OnLoadEnd() ok')")
187-
self.OnLoadEnd_called = True
213+
self.OnLoadEnd_True = True
188214

189215
# noinspection PyUnusedLocal
190216
def OnConsoleMessage(self, browser, message, source, line):
191217
if "error" in message.lower() or "uncaught" in message.lower():
192-
self.javascript_errors += 1
218+
self.javascript_errors_False = True
193219
raise Exception(message)
220+
else:
221+
# Confirmation that messages from javascript are coming
222+
self.OnConsoleMessage_True = True
223+
subtest_message(message)
194224

195225

196-
class FrameSourceVisitor:
197-
client_handler = None
198-
test_case = None
226+
class FrameSourceVisitor(object):
227+
"""Visitor for Frame.GetSource()."""
199228

200229
def __init__(self, client_handler, test_case):
201230
self.client_handler = client_handler
@@ -205,38 +234,39 @@ def __init__(self, client_handler, test_case):
205234
def Visit(self, value):
206235
self.test_case.assertIn("747ef3e6011b6a61e6b3c6e54bdd2dee",
207236
g_datauri_data)
208-
self.client_handler.FrameSourceVisitor_called = True
237+
self.client_handler.FrameSourceVisitor_True = True
209238

210239

211-
class External:
240+
class External(object):
212241
"""Javascript 'window.external' object."""
213-
test_case = None
214-
215-
# Test binding properties to the 'window' object.
216-
test_property1 = "Test binding property to the 'window' object"
217-
test_property2 = {"key1": test_property1,
218-
"key2": ["Inside list", 1, 2]}
219-
220-
test_function_called = False
221-
test_callbacks_called = False
222-
py_callback_called = False
223242

224243
def __init__(self, test_case):
225244
self.test_case = test_case
226245

246+
# Test binding properties to the 'window' object.
247+
self.test_property1 = "Test binding property to the 'window' object"
248+
self.test_property2 = {"key1": self.test_property1,
249+
"key2": ["Inside list", 1, 2]}
250+
251+
# Asserts for True/False will be checked just before shutdown
252+
self.test_for_True = True # Test whether asserts are working correctly
253+
self.test_function_True = False
254+
self.test_callbacks_True = False
255+
self.py_callback_True = False
256+
227257
def test_function(self):
228258
"""Test binding function to the 'window' object."""
229-
self.test_function_called = True
259+
self.test_function_True = True
230260

231261
def test_callbacks(self, js_callback):
232262
"""Test both javascript and python callbacks."""
233263
def py_callback(msg_from_js):
234264
self.test_case.assertEqual(msg_from_js,
235265
"String sent from Javascript")
236-
self.py_callback_called = True
266+
self.py_callback_True = True
237267
js_callback.Call("String sent from Python", py_callback)
238-
self.test_callbacks_called = True
268+
self.test_callbacks_True = True
239269

240270

241271
if __name__ == "__main__":
242-
_runner.main(basename(__file__))
272+
_test_runner.main(basename(__file__))

0 commit comments

Comments
 (0)