Skip to content

Commit d0f5129

Browse files
committed
Add initial JUnit test support (see issue chromiumembedded#339).
Unit tests can be run from the command line using the run_tests.[bat|sh] script. Currently supported on Windows and Linux only. Known issues: - Linux: TestFrame hangs due to the onBeforeClose event not being delivered.
1 parent 6b140ef commit d0f5129

13 files changed

Lines changed: 648 additions & 11 deletions
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
2+
// reserved. Use of this source code is governed by a BSD-style license that
3+
// can be found in the LICENSE file.
4+
5+
package tests.junittests;
6+
7+
import static org.junit.jupiter.api.Assertions.assertNotNull;
8+
import static org.junit.jupiter.api.Assertions.assertNull;
9+
10+
import static tests.junittests.TestSetupContext.debugPrint;
11+
12+
import org.cef.CefApp;
13+
import org.cef.CefClient;
14+
import org.cef.browser.CefBrowser;
15+
import org.cef.browser.CefFrame;
16+
import org.cef.callback.CefAuthCallback;
17+
import org.cef.callback.CefRequestCallback;
18+
import org.cef.handler.CefLifeSpanHandler;
19+
import org.cef.handler.CefLoadHandler;
20+
import org.cef.handler.CefRequestHandler;
21+
import org.cef.handler.CefResourceHandler;
22+
import org.cef.misc.BoolRef;
23+
import org.cef.misc.StringRef;
24+
import org.cef.network.CefRequest;
25+
import org.cef.network.CefRequest.TransitionType;
26+
import org.cef.network.CefResponse;
27+
import org.cef.network.CefURLRequest;
28+
29+
import java.awt.BorderLayout;
30+
import java.awt.Component;
31+
import java.awt.event.WindowAdapter;
32+
import java.awt.event.WindowEvent;
33+
import java.util.HashMap;
34+
import java.util.concurrent.CountDownLatch;
35+
36+
import javax.swing.JFrame;
37+
38+
// Base class for browsers that run tests.
39+
class TestFrame extends JFrame implements CefLifeSpanHandler, CefLoadHandler, CefRequestHandler {
40+
private static final long serialVersionUID = -5570653778104813836L;
41+
private boolean isClosed_ = false;
42+
private CountDownLatch countdown_ = new CountDownLatch(1);
43+
44+
// Resources that have been populated for the test.
45+
private class ResourceContent {
46+
String content;
47+
String mimeType;
48+
HashMap<String, String> headerMap;
49+
}
50+
private HashMap<String, ResourceContent> resourceMap_ = null;
51+
52+
protected final CefClient client_;
53+
protected CefBrowser browser_ = null;
54+
55+
TestFrame() {
56+
client_ = CefApp.getInstance().createClient();
57+
assertNotNull(client_);
58+
59+
// Browser window closing works as follows:
60+
// 1. Sending WindowEvent.WINDOW_CLOSING calls WindowAdapter.windowClosing.
61+
// 2. WindowAdapter.windowClosing calls CefBrowser.close(false).
62+
// 3. CEF calls CefLifeSpanHandler.doClose() which calls CefBrowser.doClose()
63+
// which returns true (canceling the close).
64+
// 4. CefBrowser.doClose() triggers another call to WindowAdapter.windowClosing.
65+
// 5. WindowAdapter.windowClosing calls CefBrowser.close(true).
66+
// 6. For windowed browsers CEF destroys the native window handle. For OSR
67+
// browsers CEF calls CefLifeSpanHandler.doClose() which calls
68+
// CefBrowser.doClose() again which returns false (allowing the close).
69+
// 7. CEF calls CefLifeSpanHandler.onBeforeClose and the browser is destroyed.
70+
addWindowListener(new WindowAdapter() {
71+
@Override
72+
public void windowClosing(WindowEvent e) {
73+
boolean isClosed = isClosed_;
74+
75+
if (isClosed) {
76+
// Cause browser.doClose() to return false so that OSR browsers
77+
// can close.
78+
browser_.setCloseAllowed();
79+
}
80+
81+
// Results in another call to this method.
82+
if (debugPrint())
83+
System.out.println("windowClosing CefBrowser.close(" + isClosed + ")");
84+
browser_.close(isClosed);
85+
if (!isClosed_) {
86+
isClosed_ = true;
87+
}
88+
if (isClosed) {
89+
// Dispose after the 2nd call to this method.
90+
if (debugPrint()) System.out.println("windowClosing Frame.dispose");
91+
dispose();
92+
}
93+
}
94+
});
95+
96+
client_.addLifeSpanHandler(this);
97+
client_.addLoadHandler(this);
98+
client_.addRequestHandler(this);
99+
100+
setupTest();
101+
}
102+
103+
protected void createBrowser(String startURL) {
104+
assertNull(browser_);
105+
browser_ = client_.createBrowser(startURL, false /* useOSR */, false /* isTransparent */);
106+
assertNotNull(browser_);
107+
108+
getContentPane().add(browser_.getUIComponent(), BorderLayout.CENTER);
109+
pack();
110+
setSize(800, 600);
111+
setVisible(true);
112+
}
113+
114+
// Override this method to perform test setup.
115+
protected void setupTest() {}
116+
117+
// Override this method to perform test cleanup.
118+
protected void cleanupTest() {
119+
if (debugPrint()) System.out.println("cleanupTest");
120+
client_.dispose();
121+
// Allow the test to complete.
122+
countdown_.countDown();
123+
}
124+
125+
// Call this method to terminate the test by dispatching a window close event.
126+
protected final void terminateTest() {
127+
if (debugPrint()) System.out.println("terminateTest");
128+
if (!isClosed_) {
129+
dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
130+
}
131+
}
132+
133+
// Block until the test completes.
134+
public final void awaitCompletion() {
135+
try {
136+
countdown_.await();
137+
} catch (InterruptedException e) {
138+
}
139+
if (debugPrint()) System.out.println("awaitCompletion returned");
140+
}
141+
142+
protected void addResource(String url, String content, String mimeType) {
143+
addResource(url, content, mimeType, null);
144+
}
145+
146+
// Add a resource that will be returned via getResourceHandler().
147+
protected void addResource(
148+
String url, String content, String mimeType, HashMap<String, String> headerMap) {
149+
if (resourceMap_ == null) resourceMap_ = new HashMap<>();
150+
151+
assertNull(resourceMap_.get(url));
152+
153+
ResourceContent rc = new ResourceContent();
154+
rc.content = content;
155+
rc.mimeType = mimeType;
156+
rc.headerMap = headerMap;
157+
158+
resourceMap_.put(url, rc);
159+
}
160+
161+
// CefLifeSpanHandler methods:
162+
163+
@Override
164+
public void onAfterCreated(CefBrowser browser) {
165+
if (debugPrint()) System.out.println("onAfterCreated id=" + browser.getIdentifier());
166+
}
167+
168+
@Override
169+
public boolean onBeforePopup(
170+
CefBrowser browser, CefFrame frame, String target_url, String target_frame_name) {
171+
return false;
172+
}
173+
174+
@Override
175+
public void onAfterParentChanged(CefBrowser browser) {}
176+
177+
@Override
178+
public boolean doClose(CefBrowser browser) {
179+
boolean result = browser.doClose();
180+
if (debugPrint()) {
181+
System.out.println(
182+
"doClose id=" + browser.getIdentifier() + " CefBrowser.doClose=" + result);
183+
}
184+
return result;
185+
}
186+
187+
@Override
188+
public void onBeforeClose(CefBrowser browser) {
189+
if (debugPrint()) System.out.println("onBeforeClose id=" + browser.getIdentifier());
190+
cleanupTest();
191+
}
192+
193+
// CefLoadHandler methods:
194+
195+
@Override
196+
public void onLoadingStateChange(
197+
CefBrowser browser, boolean isLoading, boolean canGoBack, boolean canGoForward) {}
198+
199+
@Override
200+
public void onLoadStart(CefBrowser browser, CefFrame frame, TransitionType transitionType) {}
201+
202+
@Override
203+
public void onLoadEnd(CefBrowser browser, CefFrame frame, int httpStatusCode) {}
204+
205+
@Override
206+
public void onLoadError(CefBrowser browser, CefFrame frame, ErrorCode errorCode,
207+
String errorText, String failedUrl) {}
208+
209+
// CefRequestHandler methods:
210+
211+
@Override
212+
public boolean onBeforeBrowse(CefBrowser browser, CefFrame frame, CefRequest request,
213+
boolean user_gesture, boolean is_redirect) {
214+
return false;
215+
}
216+
217+
@Override
218+
public boolean onBeforeResourceLoad(CefBrowser browser, CefFrame frame, CefRequest request) {
219+
return false;
220+
}
221+
222+
@Override
223+
public CefResourceHandler getResourceHandler(
224+
CefBrowser browser, CefFrame frame, CefRequest request) {
225+
if (resourceMap_ != null) {
226+
String url = request.getURL();
227+
228+
// Ignore the query component, if any.
229+
int idx = url.indexOf('?');
230+
if (idx > 0) url = url.substring(0, idx);
231+
232+
ResourceContent rc = resourceMap_.get(url);
233+
if (rc != null) {
234+
if (debugPrint()) System.out.println("Found resource for: " + url);
235+
return new TestResourceHandler(rc.content, rc.mimeType, rc.headerMap);
236+
}
237+
}
238+
239+
return null;
240+
}
241+
242+
@Override
243+
public void onResourceRedirect(CefBrowser browser, CefFrame frame, CefRequest request,
244+
CefResponse response, StringRef new_url) {}
245+
246+
@Override
247+
public boolean onResourceResponse(
248+
CefBrowser browser, CefFrame frame, CefRequest request, CefResponse response) {
249+
return false;
250+
}
251+
252+
@Override
253+
public void onResourceLoadComplete(CefBrowser browser, CefFrame frame, CefRequest request,
254+
CefResponse response, CefURLRequest.Status status, long receivedContentLength) {}
255+
256+
@Override
257+
public boolean getAuthCredentials(CefBrowser browser, CefFrame frame, boolean isProxy,
258+
String host, int port, String realm, String scheme, CefAuthCallback callback) {
259+
return false;
260+
}
261+
262+
@Override
263+
public boolean onQuotaRequest(
264+
CefBrowser browser, String origin_url, long new_size, CefRequestCallback callback) {
265+
return false;
266+
}
267+
268+
@Override
269+
public void onProtocolExecution(CefBrowser browser, String url, BoolRef allow_os_execution) {}
270+
271+
@Override
272+
public boolean onCertificateError(CefBrowser browser, CefLoadHandler.ErrorCode cert_error,
273+
String request_url, CefRequestCallback callback) {
274+
return false;
275+
}
276+
277+
@Override
278+
public void onPluginCrashed(CefBrowser browser, String pluginPath) {}
279+
280+
@Override
281+
public void onRenderProcessTerminated(CefBrowser browser, TerminationStatus status) {}
282+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
2+
// reserved. Use of this source code is governed by a BSD-style license that
3+
// can be found in the LICENSE file.
4+
5+
package tests.junittests;
6+
7+
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
import static org.junit.jupiter.api.Assertions.assertFalse;
9+
import static org.junit.jupiter.api.Assertions.assertTrue;
10+
11+
import org.cef.browser.CefBrowser;
12+
import org.cef.handler.CefDisplayHandlerAdapter;
13+
import org.cef.handler.CefLoadHandlerAdapter;
14+
import org.junit.jupiter.api.Test;
15+
import org.junit.jupiter.api.extension.ExtendWith;
16+
17+
// Test the TestFrame implementation.
18+
@ExtendWith(TestSetupExtension.class)
19+
class TestFrameTest {
20+
private boolean gotSetupTest_ = false;
21+
private boolean gotCleanupTest_ = false;
22+
private boolean gotLoadingStateChange_ = false;
23+
24+
@Test
25+
void minimal() {
26+
final String testUrl = "http://test.com/test.html";
27+
TestFrame frame = new TestFrame() {
28+
@Override
29+
protected void setupTest() {
30+
assertFalse(gotSetupTest_);
31+
gotSetupTest_ = true;
32+
33+
addResource(testUrl, "<html><body>Test!</body></html>", "test/html");
34+
35+
createBrowser(testUrl);
36+
37+
super.setupTest();
38+
}
39+
40+
@Override
41+
protected void cleanupTest() {
42+
assertFalse(gotCleanupTest_);
43+
gotCleanupTest_ = true;
44+
45+
super.cleanupTest();
46+
}
47+
48+
@Override
49+
public void onLoadingStateChange(CefBrowser browser, boolean isLoading,
50+
boolean canGoBack, boolean canGoForward) {
51+
if (!isLoading) {
52+
assertFalse(gotLoadingStateChange_);
53+
gotLoadingStateChange_ = true;
54+
terminateTest();
55+
}
56+
}
57+
};
58+
59+
frame.awaitCompletion();
60+
61+
assertTrue(gotSetupTest_);
62+
assertTrue(gotLoadingStateChange_);
63+
assertTrue(gotCleanupTest_);
64+
}
65+
}

0 commit comments

Comments
 (0)