Skip to content

Commit ce72fcd

Browse files
committed
Ensure iOS sample renders before screenshots
1 parent c2d6fc9 commit ce72fcd

File tree

2 files changed

+66
-15
lines changed

2 files changed

+66
-15
lines changed

scripts/ios/tests/HelloCodenameOneUITests.swift.tmpl

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ final class HelloCodenameOneUITests: XCTestCase {
66
private var app: XCUIApplication!
77
private var outputDirectory: URL!
88
private var targetBundleIdentifier: String?
9+
private var resolvedBundleIdentifier: String?
910
private let codenameOneSurfaceIdentifier = "cn1.glview"
1011
private let chunkSize = 2000
1112
private let previewChannel = "PREVIEW"
@@ -40,6 +41,12 @@ final class HelloCodenameOneUITests: XCTestCase {
4041
let reportedBundleId = targetBundleIdentifier ?? "(scheme-default)"
4142
print("CN1SS:INFO:ui_test_target_bundle_id=\(reportedBundleId)")
4243
print("CN1SS:INFO:ui_test_launch_arguments=\(app.launchArguments.joined(separator: " "))")
44+
if let resolved = resolveBundleIdentifier() {
45+
resolvedBundleIdentifier = resolved
46+
print("CN1SS:INFO:ui_test_resolved_bundle_id=\(resolved)")
47+
} else {
48+
print("CN1SS:WARN:ui_test_resolved_bundle_id_unavailable=true")
49+
}
4350

4451
let launchSurface = waitForCodenameOneSurface(timeout: 40, context: "post_launch")
4552
logSurfaceMetrics(launchSurface, context: "post_launch")
@@ -118,19 +125,21 @@ final class HelloCodenameOneUITests: XCTestCase {
118125
var attempts = 0
119126
while true {
120127
attempts += 1
121-
let screenshot = XCUIScreen.main.screenshot()
122-
let hasContent = screenshotHasRenderableContent(screenshot)
128+
let screenshot = app.screenshot()
129+
let analysis = analyzeScreenshot(screenshot)
130+
let hasContent = analysis.hasRenderableContent
123131
if hasContent {
124132
print("CN1SS:INFO:test=\(label) rendered_frame_detected_attempt=\(attempts)")
125133
return (screenshot, true, attempts)
126134
}
127135

128136
let now = Date()
129137
if now >= deadline {
138+
print("CN1SS:INFO:test=\(label) rendered_frame_luma_variance=\(analysis.lumaVariance)")
130139
return (screenshot, false, attempts)
131140
}
132141

133-
print("CN1SS:INFO:test=\(label) waiting_for_rendered_frame attempt=\(attempts)")
142+
print("CN1SS:INFO:test=\(label) waiting_for_rendered_frame attempt=\(attempts) luma_variance=\(analysis.lumaVariance)")
134143
let nextInterval = min(poll, deadline.timeIntervalSince(now))
135144
RunLoop.current.run(until: Date(timeIntervalSinceNow: nextInterval))
136145
}
@@ -143,7 +152,7 @@ final class HelloCodenameOneUITests: XCTestCase {
143152
if exists {
144153
return surface
145154
}
146-
let fallback = XCUIScreen.main.screenshot()
155+
let fallback = app.screenshot()
147156
let screenshotAttachment = XCTAttachment(screenshot: fallback)
148157
screenshotAttachment.name = "\(context)_missing_surface_screen"
149158
screenshotAttachment.lifetime = .keepAlways
@@ -170,14 +179,14 @@ final class HelloCodenameOneUITests: XCTestCase {
170179
add(attachment)
171180
}
172181

173-
private func screenshotHasRenderableContent(_ screenshot: XCUIScreenshot) -> Bool {
182+
private func analyzeScreenshot(_ screenshot: XCUIScreenshot) -> (hasRenderableContent: Bool, lumaVariance: Int) {
174183
guard let cgImage = screenshot.image.cgImage else {
175-
return true
184+
return (true, 255)
176185
}
177186
let width = cgImage.width
178187
let height = cgImage.height
179188
guard width > 0, height > 0 else {
180-
return false
189+
return (false, 0)
181190
}
182191

183192
let insetX = max(0, width / 8)
@@ -189,7 +198,7 @@ final class HelloCodenameOneUITests: XCTestCase {
189198
height: max(1, height - insetY * 2)
190199
).integral
191200
guard let cropped = cgImage.cropping(to: cropRect) else {
192-
return true
201+
return (true, 255)
193202
}
194203

195204
let sampleWidth = 80
@@ -205,12 +214,12 @@ final class HelloCodenameOneUITests: XCTestCase {
205214
space: CGColorSpaceCreateDeviceRGB(),
206215
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
207216
) else {
208-
return true
217+
return (true, 255)
209218
}
210219
context.interpolationQuality = .high
211220
context.draw(cropped, in: CGRect(x: 0, y: 0, width: sampleWidth, height: sampleHeight))
212221
guard let data = context.data else {
213-
return true
222+
return (true, 255)
214223
}
215224

216225
let buffer = data.bindMemory(to: UInt8.self, capacity: sampleHeight * bytesPerRow)
@@ -229,7 +238,23 @@ final class HelloCodenameOneUITests: XCTestCase {
229238
}
230239
}
231240

232-
return maxLuma - minLuma > 12
241+
let variance = maxLuma - minLuma
242+
return (variance > 12, variance)
243+
}
244+
245+
private func resolveBundleIdentifier() -> String? {
246+
if let explicit = targetBundleIdentifier, !explicit.isEmpty {
247+
return explicit
248+
}
249+
do {
250+
let value = try app.value(forKey: "bundleID")
251+
if let actual = value as? String, !actual.isEmpty {
252+
return actual
253+
}
254+
} catch {
255+
print("CN1SS:WARN:ui_test_bundle_resolution_failed error=\(error)")
256+
}
257+
return nil
233258
}
234259

235260
private func sanitizeTestName(_ name: String) -> String {

scripts/templates/HelloCodenameOne.java.tmpl

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,18 @@ import com.codename1.ui.layouts.BoxLayout;
1313
public class @MAIN_NAME@ {
1414
private Form current;
1515
private Form mainForm;
16+
private boolean initBootstrapScheduled;
1617

1718
public void init(Object context) {
1819
System.out.println("CN1SS:INFO:codenameone_init context=" + (context == null ? "null" : context.getClass().getName()));
1920
System.out.println("CN1SS:INFO:codenameone_init_display_initialized=" + Display.isInitialized());
20-
// No special initialization required for this sample
21+
if (!initBootstrapScheduled) {
22+
initBootstrapScheduled = true;
23+
Display.getInstance().callSerially(() -> {
24+
System.out.println("CN1SS:INFO:codenameone_init_serial_bootstrap begin=true");
25+
ensureMainFormVisible("init_serial");
26+
});
27+
}
2128
}
2229

2330
public void start() {
@@ -42,7 +49,7 @@ public class @MAIN_NAME@ {
4249
current.show();
4350
return;
4451
}
45-
showMainForm();
52+
ensureMainFormVisible("start");
4653
}
4754

4855
public void stop() {
@@ -58,7 +65,7 @@ public class @MAIN_NAME@ {
5865
// Nothing to clean up for this sample
5966
}
6067

61-
private void showMainForm() {
68+
private void buildMainFormIfNeeded() {
6269
if (mainForm == null) {
6370
System.out.println("CN1SS:INFO:codenameone_build_main_form start=true");
6471
mainForm = new Form("Main Screen", new BorderLayout());
@@ -89,6 +96,25 @@ public class @MAIN_NAME@ {
8996
mainForm.add(BorderLayout.CENTER, content);
9097
System.out.println("CN1SS:INFO:codenameone_build_main_form complete=true");
9198
}
99+
}
100+
101+
private void ensureMainFormVisible(String reason) {
102+
buildMainFormIfNeeded();
103+
Display display = Display.getInstance();
104+
Form visible = display.getCurrent();
105+
if (visible != mainForm) {
106+
current = mainForm;
107+
mainForm.show();
108+
System.out.println("CN1SS:INFO:codenameone_main_form_presented reason=" + reason);
109+
} else {
110+
current = mainForm;
111+
System.out.println("CN1SS:INFO:codenameone_main_form_already_visible reason=" + reason);
112+
}
113+
Display.getInstance().callSerially(() -> logFormMetrics("codenameone_main_form_ready", Display.getInstance().getCurrent()));
114+
}
115+
116+
private void showMainForm() {
117+
buildMainFormIfNeeded();
92118
current = mainForm;
93119
mainForm.show();
94120
System.out.println("CN1SS:INFO:codenameone_main_form_shown title=" + mainForm.getTitle());
@@ -109,7 +135,7 @@ public class @MAIN_NAME@ {
109135
FontImage.MATERIAL_ARROW_BACK,
110136
evt -> {
111137
System.out.println("CN1SS:INFO:codenameone_browser_back_action triggered=true");
112-
showMainForm();
138+
ensureMainFormVisible("browser_back");
113139
}
114140
);
115141

0 commit comments

Comments
 (0)