1313import androidx .test .core .app .ApplicationProvider ;
1414import androidx .test .ext .junit .runners .AndroidJUnit4 ;
1515
16+ import com .codename1 .ui .BrowserComponent ;
17+ import com .codename1 .ui .Display ;
18+ import com .codename1 .ui .Form ;
19+ import com .codename1 .ui .layouts .BorderLayout ;
20+
1621import org .junit .Assert ;
22+ import org .junit .Assume ;
1723import org .junit .Test ;
1824import org .junit .runner .RunWith ;
1925
2026import java .io .ByteArrayOutputStream ;
27+ import java .util .Locale ;
28+ import java .util .concurrent .CountDownLatch ;
29+ import java .util .concurrent .TimeUnit ;
2130
2231@ RunWith (AndroidJUnit4 .class )
2332public class HelloCodenameOneInstrumentedTest {
2433
34+ private static final int CHUNK_SIZE = 2000 ;
35+ private static final String MAIN_SCREEN_TEST = "MainActivity" ;
36+ private static final String BROWSER_TEST = "BrowserComponent" ;
37+
2538 private static void println (String s ) {
2639 System .out .println (s );
2740 }
2841
29- @ Test
30- public void testUseAppContext_andEmitScreenshot () throws Exception {
31- Context ctx = ApplicationProvider .getApplicationContext ();
32- String pkg = "@PACKAGE@" ;
33- Assert .assertEquals ("Package mismatch" , pkg , ctx .getPackageName ());
42+ private static void settle (long millis ) {
43+ try {
44+ Thread .sleep (millis );
45+ } catch (InterruptedException ie ) {
46+ Thread .currentThread ().interrupt ();
47+ }
48+ }
3449
50+ private static ActivityScenario <Activity > launchMainActivity (Context ctx ) {
51+ String pkg = "@PACKAGE@" ;
3552 Intent launch = ctx .getPackageManager ().getLaunchIntentForPackage (pkg );
3653 if (launch == null ) {
3754 Intent q = new Intent (Intent .ACTION_MAIN );
@@ -40,73 +57,148 @@ public void testUseAppContext_andEmitScreenshot() throws Exception {
4057 launch = q ;
4158 }
4259 launch .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK );
60+ println ("CN1SS:INFO: launching activity for test" );
61+ return ActivityScenario .launch (launch );
62+ }
4363
44- println ("CN1SS:INFO: about to launch Activity" );
45- byte [] pngBytes = null ;
46-
47- try (ActivityScenario <Activity > scenario = ActivityScenario .launch (launch )) {
48- Thread .sleep (750 );
49-
50- println ("CN1SS:INFO: activity launched" );
51-
52- final byte [][] holder = new byte [1 ][];
53- scenario .onActivity (activity -> {
54- try {
55- View root = activity .getWindow ().getDecorView ().getRootView ();
56- int w = root .getWidth ();
57- int h = root .getHeight ();
58- if (w <= 0 || h <= 0 ) {
59- DisplayMetrics dm = activity .getResources ().getDisplayMetrics ();
60- w = Math .max (1 , dm .widthPixels );
61- h = Math .max (1 , dm .heightPixels );
62- int sw = View .MeasureSpec .makeMeasureSpec (w , View .MeasureSpec .EXACTLY );
63- int sh = View .MeasureSpec .makeMeasureSpec (h , View .MeasureSpec .EXACTLY );
64- root .measure (sw , sh );
65- root .layout (0 , 0 , w , h );
66- println ("CN1SS:INFO: forced layout to " + w + "x" + h );
67- } else {
68- println ("CN1SS:INFO: natural layout " + w + "x" + h );
69- }
70-
71- Bitmap bmp = Bitmap .createBitmap (w , h , Bitmap .Config .ARGB_8888 );
72- Canvas c = new Canvas (bmp );
73- root .draw (c );
74-
75- ByteArrayOutputStream baos = new ByteArrayOutputStream (Math .max (1024 , w * h / 2 ));
76- boolean ok = bmp .compress (Bitmap .CompressFormat .PNG , 100 , baos );
77- if (!ok ) {
78- throw new RuntimeException ("Bitmap.compress returned false" );
79- }
80- holder [0 ] = baos .toByteArray ();
81- println ("CN1SS:INFO: png_bytes=" + holder [0 ].length );
82- } catch (Throwable t ) {
83- println ("CN1SS:ERR: onActivity " + t );
84- t .printStackTrace (System .out );
64+ private static byte [] captureScreenshot (ActivityScenario <Activity > scenario , String testName ) {
65+ final byte [][] holder = new byte [1 ][];
66+ scenario .onActivity (activity -> {
67+ try {
68+ View root = activity .getWindow ().getDecorView ().getRootView ();
69+ int w = root .getWidth ();
70+ int h = root .getHeight ();
71+ if (w <= 0 || h <= 0 ) {
72+ DisplayMetrics dm = activity .getResources ().getDisplayMetrics ();
73+ w = Math .max (1 , dm .widthPixels );
74+ h = Math .max (1 , dm .heightPixels );
75+ int sw = View .MeasureSpec .makeMeasureSpec (w , View .MeasureSpec .EXACTLY );
76+ int sh = View .MeasureSpec .makeMeasureSpec (h , View .MeasureSpec .EXACTLY );
77+ root .measure (sw , sh );
78+ root .layout (0 , 0 , w , h );
79+ println ("CN1SS:INFO:test=" + testName + " forced layout to " + w + "x" + h );
80+ } else {
81+ println ("CN1SS:INFO:test=" + testName + " natural layout " + w + "x" + h );
8582 }
86- });
8783
88- pngBytes = holder [0 ];
89- } catch (Throwable t ) {
90- println ("CN1SS:ERR: launch " + t );
91- t .printStackTrace (System .out );
92- }
84+ Bitmap bmp = Bitmap .createBitmap (w , h , Bitmap .Config .ARGB_8888 );
85+ Canvas c = new Canvas (bmp );
86+ root .draw (c );
87+
88+ ByteArrayOutputStream baos = new ByteArrayOutputStream (Math .max (1024 , w * h / 2 ));
89+ if (!bmp .compress (Bitmap .CompressFormat .PNG , 100 , baos )) {
90+ throw new RuntimeException ("Bitmap.compress returned false" );
91+ }
92+ holder [0 ] = baos .toByteArray ();
93+ println ("CN1SS:INFO:test=" + testName + " png_bytes=" + holder [0 ].length );
94+ } catch (Throwable t ) {
95+ println ("CN1SS:ERR:test=" + testName + " " + t );
96+ t .printStackTrace (System .out );
97+ }
98+ });
99+ return holder [0 ];
100+ }
101+
102+ private static String sanitizeTestName (String testName ) {
103+ return testName .replaceAll ("[^A-Za-z0-9_.-]" , "_" );
104+ }
93105
106+ private static void emitScreenshot (byte [] pngBytes , String testName ) {
107+ String safeName = sanitizeTestName (testName );
94108 if (pngBytes == null || pngBytes .length == 0 ) {
95- println ("CN1SS:END" );
96- Assert .fail ("Screenshot capture produced 0 bytes" );
109+ println ("CN1SS:END:" + safeName );
110+ Assert .fail ("Screenshot capture produced 0 bytes for " + testName );
97111 return ;
98112 }
99-
100113 String b64 = Base64 .encodeToString (pngBytes , Base64 .NO_WRAP );
101- final int chunkSize = 2000 ;
102114 int count = 0 ;
103- for (int pos = 0 ; pos < b64 .length (); pos += chunkSize ) {
104- int end = Math .min (pos + chunkSize , b64 .length ());
105- System .out .println ("CN1SS:" + String .format ("%06d" , pos ) + ":" + b64 .substring (pos , end ));
115+ for (int pos = 0 ; pos < b64 .length (); pos += CHUNK_SIZE ) {
116+ int end = Math .min (pos + CHUNK_SIZE , b64 .length ());
117+ String chunk = b64 .substring (pos , end );
118+ System .out .println ("CN1SS:" + safeName + ":" + String .format (Locale .US , "%06d" , pos ) + ":" + chunk );
106119 count ++;
107120 }
108- println ("CN1SS:INFO: chunks=" + count + " total_b64_len=" + b64 .length ());
109- System .out .println ("CN1SS:END" );
121+ println ("CN1SS:INFO:test=" + safeName + " chunks=" + count + " total_b64_len=" + b64 .length ());
122+ System .out .println ("CN1SS:END:" + safeName );
110123 System .out .flush ();
111124 }
125+
126+ private static void prepareBrowserComponentContent (ActivityScenario <Activity > scenario ) throws InterruptedException {
127+ final CountDownLatch supportLatch = new CountDownLatch (1 );
128+ final boolean [] supported = new boolean [1 ];
129+
130+ scenario .onActivity (activity -> Display .getInstance ().callSerially (() -> {
131+ try {
132+ supported [0 ] = BrowserComponent .isNativeBrowserSupported ();
133+ } finally {
134+ supportLatch .countDown ();
135+ }
136+ }));
137+
138+ if (!supportLatch .await (5 , TimeUnit .SECONDS )) {
139+ Assert .fail ("Timed out while verifying BrowserComponent support" );
140+ }
141+
142+ Assume .assumeTrue ("BrowserComponent native support required for this test" , supported [0 ]);
143+
144+ final CountDownLatch loadLatch = new CountDownLatch (1 );
145+ final String html = "<html><head><meta charset='utf-8'/>"
146+ + "<style>body{margin:0;font-family:sans-serif;background:#0e1116;color:#f3f4f6;}"
147+ + ".container{padding:24px;text-align:center;}h1{font-size:24px;margin-bottom:12px;}"
148+ + "p{font-size:16px;line-height:1.4;}span{color:#4cc9f0;}</style></head>"
149+ + "<body><div class='container'><h1>Codename One</h1>"
150+ + "<p>BrowserComponent <span>instrumentation</span> test content.</p></div></body></html>" ;
151+
152+ scenario .onActivity (activity -> Display .getInstance ().callSerially (() -> {
153+ Form current = Display .getInstance ().getCurrent ();
154+ if (current == null ) {
155+ current = new Form ("Browser Test" , new BorderLayout ());
156+ current .show ();
157+ } else {
158+ current .setLayout (new BorderLayout ());
159+ current .setTitle ("Browser Test" );
160+ current .removeAll ();
161+ }
162+
163+ BrowserComponent browser = new BrowserComponent ();
164+ browser .addWebEventListener (BrowserComponent .onLoad , evt -> loadLatch .countDown ());
165+ browser .setPage (html , null );
166+ current .add (BorderLayout .CENTER , browser );
167+ current .revalidate ();
168+ }));
169+
170+ if (!loadLatch .await (10 , TimeUnit .SECONDS )) {
171+ Assert .fail ("Timed out waiting for BrowserComponent to load content" );
172+ }
173+ }
174+
175+ @ Test
176+ public void testUseAppContext_andEmitScreenshot () throws Exception {
177+ Context ctx = ApplicationProvider .getApplicationContext ();
178+ String pkg = "@PACKAGE@" ;
179+ Assert .assertEquals ("Package mismatch" , pkg , ctx .getPackageName ());
180+
181+ byte [] pngBytes ;
182+ try (ActivityScenario <Activity > scenario = launchMainActivity (ctx )) {
183+ settle (750 );
184+ pngBytes = captureScreenshot (scenario , MAIN_SCREEN_TEST );
185+ }
186+
187+ emitScreenshot (pngBytes , MAIN_SCREEN_TEST );
188+ }
189+
190+ @ Test
191+ public void testBrowserComponentScreenshot () throws Exception {
192+ Context ctx = ApplicationProvider .getApplicationContext ();
193+ byte [] pngBytes ;
194+
195+ try (ActivityScenario <Activity > scenario = launchMainActivity (ctx )) {
196+ settle (750 );
197+ prepareBrowserComponentContent (scenario );
198+ settle (500 );
199+ pngBytes = captureScreenshot (scenario , BROWSER_TEST );
200+ }
201+
202+ emitScreenshot (pngBytes , BROWSER_TEST );
203+ }
112204}
0 commit comments