@@ -29,10 +29,6 @@ import org.junit.runner.RunWith
2929 * Prerequisites:
3030 * - Push a .pte model file to /data/local/tmp/llama/
3131 * - Push a tokenizer file (.bin, .json, or .model) to /data/local/tmp/llama/
32- *
33- * Model filenames can be configured via instrumentation arguments:
34- * - modelFile: name of the .pte file (default: stories110M.pte)
35- * - tokenizerFile: name of the tokenizer file (default: tokenizer.model)
3632 */
3733@RunWith(AndroidJUnit4 ::class )
3834@LargeTest
@@ -43,6 +39,7 @@ class UIWorkflowTest {
4339 private const val DEFAULT_TOKENIZER_FILE = " tokenizer.model"
4440 private const val TIMEOUT_MS = 5000L
4541 private const val LONG_TIMEOUT_MS = 60000L
42+ private const val PACKAGE_NAME = " com.example.executorchllamademo"
4643 }
4744
4845 private lateinit var device: UiDevice
@@ -53,13 +50,12 @@ class UIWorkflowTest {
5350 fun setUp () {
5451 device = UiDevice .getInstance(InstrumentationRegistry .getInstrumentation())
5552
56- // Read model filenames from instrumentation arguments
5753 val args = InstrumentationRegistry .getArguments()
5854 modelFile = args.getString(" modelFile" , DEFAULT_MODEL_FILE ) ? : DEFAULT_MODEL_FILE
5955 tokenizerFile = args.getString(" tokenizerFile" , DEFAULT_TOKENIZER_FILE ) ? : DEFAULT_TOKENIZER_FILE
6056 Log .i(" UIWorkflowTest" , " Using model: $modelFile , tokenizer: $tokenizerFile " )
6157
62- // Clear SharedPreferences before each test
58+ // Clear SharedPreferences
6359 val context = InstrumentationRegistry .getInstrumentation().targetContext
6460 val prefs = context.getSharedPreferences(
6561 context.getString(R .string.demo_pref_file_key),
@@ -68,112 +64,93 @@ class UIWorkflowTest {
6864 prefs.edit().clear().commit()
6965 }
7066
71- /* *
72- * Tests the complete model loading workflow.
73- */
7467 @Test
7568 fun testModelLoadingWorkflow () {
7669 ActivityScenario .launch(MainActivity ::class .java)
77- Thread .sleep(1500 )
78-
79- // Dismiss the initial dialog
80- dismissInitialDialog()
81-
82- // Click settings button (find by content description)
83- val settingsButton = device.findObject(UiSelector ().descriptionContains(" Settings" ))
84- if (settingsButton.exists()) {
85- settingsButton.click()
86- } else {
87- // Try finding by the test tag approach - look for clickable elements
88- device.findObject(UiSelector ().clickable(true ).instance(1 ))?.click()
89- }
70+ device.wait(Until .hasObject(By .pkg(PACKAGE_NAME ).depth(0 )), TIMEOUT_MS )
9071 Thread .sleep(1000 )
9172
92- // Verify we're in settings - look for "Load Model" button
93- val loadModelButton = device.wait( Until .findObject( By .textContains( " Load Model " )), TIMEOUT_MS )
94- assertTrue( " Load Model button should be visible " , loadModelButton != null )
73+ // Dismiss initial dialog
74+ clickButton( " OK " )
75+ Thread .sleep( 500 )
9576
96- // Click model selection
97- val modelSection = device.findObject(UiSelector ().textContains(" Model" ).instance(0 ))
98- if (modelSection.exists()) {
99- // Find and click the file picker button near model text
100- device.findObject(UiSelector ().className(" android.widget.Button" ).instance(0 ))?.click()
101- ? : device.findObject(UiSelector ().clickable(true ).instance(2 ))?.click()
102- }
77+ // Click settings button (by content description)
78+ clickByDescription(" Settings" )
79+ Thread .sleep(1000 )
80+
81+ // Wait for settings screen
82+ val loadModelVisible = device.wait(Until .hasObject(By .text(" Load Model" )), TIMEOUT_MS )
83+ assertTrue(" Should navigate to settings screen" , loadModelVisible)
84+
85+ // Verify model/tokenizer are not selected
86+ assertTrue(" Should show no model selected" ,
87+ device.findObject(UiSelector ().textContains(" no model" )).exists())
88+ assertTrue(" Should show no tokenizer selected" ,
89+ device.findObject(UiSelector ().textContains(" no tokenizer" )).exists())
90+
91+ // Click model select (by description)
92+ clickByDescription(" Select Model" )
10393 Thread .sleep(500 )
10494
105- // Select model file from dialog
106- val modelFileItem = device.wait(Until .findObject(By .textContains(modelFile)), TIMEOUT_MS )
107- modelFileItem?.click()
95+ // Select model file
96+ device.wait(Until .findObject(By .textContains(modelFile)), TIMEOUT_MS )?.click()
10897 Thread .sleep(500 )
10998
110- // Click tokenizer selection
111- val tokenizerButton = device.findObject(UiSelector ().clickable(true ).instance(3 ))
112- tokenizerButton?.click()
99+ // Click tokenizer select
100+ clickByDescription(" Select Tokenizer" )
113101 Thread .sleep(500 )
114102
115- // Select tokenizer file from dialog
116- val tokenizerFileItem = device.wait(Until .findObject(By .textContains(tokenizerFile)), TIMEOUT_MS )
117- tokenizerFileItem?.click()
103+ // Select tokenizer file
104+ device.wait(Until .findObject(By .textContains(tokenizerFile)), TIMEOUT_MS )?.click()
118105 Thread .sleep(500 )
119106
120107 // Click Load Model button
121- val loadButton = device.findObject(UiSelector ().textContains(" Load Model" ))
122- if (loadButton.exists() && loadButton.isEnabled) {
123- loadButton.click()
124- }
108+ device.findObject(UiSelector ().text(" Load Model" ))?.click()
125109 Thread .sleep(500 )
126110
127111 // Confirm dialog
128- clickDialogButton (" OK" )
112+ clickButton (" OK" )
129113 }
130114
131- /* *
132- * Tests sending a message and receiving a response.
133- */
134115 @Test
135116 fun testSendMessageAndReceiveResponse () {
136117 ActivityScenario .launch(MainActivity ::class .java)
137- Thread .sleep(1500 )
118+ device.wait(Until .hasObject(By .pkg(PACKAGE_NAME ).depth(0 )), TIMEOUT_MS )
119+ Thread .sleep(1000 )
120+
121+ clickButton(" OK" )
122+ Thread .sleep(500 )
138123
139- dismissInitialDialog()
140124 loadModel()
141125
142126 // Wait for model to load
143127 val modelLoaded = waitForText(" Successfully loaded model" , LONG_TIMEOUT_MS )
144128 assertTrue(" Model should be loaded successfully" , modelLoaded)
145129
146- // Type message in input field
130+ // Find input field and type message
147131 val inputField = device.findObject(UiSelector ().className(" android.widget.EditText" ))
148132 if (inputField.exists()) {
149133 inputField.setText(" tell me a story" )
134+ Thread .sleep(500 )
150135 }
151- Thread .sleep(500 )
152136
153137 // Click send button
154- val sendButton = device.findObject(UiSelector ().descriptionContains(" Send" ))
155- if (sendButton.exists()) {
156- sendButton.click()
157- } else {
158- // Find clickable button near the input
159- device.findObject(UiSelector ().clickable(true ).instance(4 ))?.click()
160- }
161-
162- // Wait for response
163- Thread .sleep(10000 ) // Wait for generation
138+ clickByDescription(" Send" )
164139
140+ // Wait for response
141+ Thread .sleep(10000 )
165142 Log .i(" TEST" , " testSendMessageAndReceiveResponse completed" )
166143 }
167144
168- /* *
169- * Tests stopping generation mid-way.
170- */
171145 @Test
172146 fun testStopGeneration () {
173147 ActivityScenario .launch(MainActivity ::class .java)
174- Thread .sleep(1500 )
148+ device.wait(Until .hasObject(By .pkg(PACKAGE_NAME ).depth(0 )), TIMEOUT_MS )
149+ Thread .sleep(1000 )
150+
151+ clickButton(" OK" )
152+ Thread .sleep(500 )
175153
176- dismissInitialDialog()
177154 loadModel()
178155
179156 val modelLoaded = waitForText(" Successfully loaded model" , LONG_TIMEOUT_MS )
@@ -183,142 +160,125 @@ class UIWorkflowTest {
183160 val inputField = device.findObject(UiSelector ().className(" android.widget.EditText" ))
184161 if (inputField.exists()) {
185162 inputField.setText(" Write a very long story about a brave knight" )
163+ Thread .sleep(500 )
186164 }
187- Thread .sleep(500 )
188165
189- // Click send button
190- device.findObject(UiSelector ().descriptionContains(" Send" ))?.click()
191- ? : device.findObject(UiSelector ().clickable(true ).instance(4 ))?.click()
192-
193- // Wait for generation to start
166+ // Send
167+ clickByDescription(" Send" )
194168 Thread .sleep(3000 )
195169
196- // Click stop button (same button, now shows stop)
197- device.findObject(UiSelector ().descriptionContains(" Stop" ))?.click()
198- ? : device.findObject(UiSelector ().clickable(true ).instance(4 ))?.click()
199-
170+ // Click stop
171+ clickByDescription(" Stop" )
200172 Thread .sleep(1000 )
173+
201174 Log .i(" STOP_TEST" , " Stop generation test completed" )
202175 }
203176
204- /* *
205- * Tests empty prompt behavior.
206- */
207177 @Test
208178 fun testEmptyPromptSend () {
209179 ActivityScenario .launch(MainActivity ::class .java)
210- Thread .sleep(1500 )
180+ device.wait(Until .hasObject(By .pkg(PACKAGE_NAME ).depth(0 )), TIMEOUT_MS )
181+ Thread .sleep(1000 )
182+
183+ clickButton(" OK" )
184+ Thread .sleep(500 )
211185
212- dismissInitialDialog()
213186 loadModel()
214187
215188 val modelLoaded = waitForText(" Successfully loaded model" , LONG_TIMEOUT_MS )
216189 assertTrue(" Model should be loaded successfully" , modelLoaded)
217190
218- // Verify send button exists
219- val sendButton = device.findObject(UiSelector ().descriptionContains(" Send" ))
220- assertTrue(" Send button should exist" , sendButton.exists() ||
221- device.findObject(UiSelector ().clickable(true ).instance(4 )).exists())
222-
223- // Type some text
191+ // Verify input field exists
224192 val inputField = device.findObject(UiSelector ().className(" android.widget.EditText" ))
225- if (inputField.exists()) {
226- inputField.setText(" hello" )
227- Thread .sleep(300 )
228- inputField.clearTextField()
229- }
193+ assertTrue(" Input field should exist" , inputField.exists())
194+
195+ // Type and clear text
196+ inputField.setText(" hello" )
197+ Thread .sleep(300 )
198+ inputField.clearTextField()
199+ Thread .sleep(300 )
230200
231201 Log .i(" TEST" , " testEmptyPromptSend completed" )
232202 }
233203
234- /* *
235- * Tests file selection dialogs.
236- */
237204 @Test
238205 fun testFileSelectionDialogs () {
239206 ActivityScenario .launch(MainActivity ::class .java)
240- Thread .sleep(1500 )
207+ device.wait(Until .hasObject(By .pkg(PACKAGE_NAME ).depth(0 )), TIMEOUT_MS )
208+ Thread .sleep(1000 )
241209
242- dismissInitialDialog()
210+ clickButton(" OK" )
211+ Thread .sleep(500 )
243212
244213 // Go to settings
245- device.findObject(UiSelector ().descriptionContains(" Settings" ))?.click()
246- ? : device.findObject(UiSelector ().clickable(true ).instance(1 ))?.click()
214+ clickByDescription(" Settings" )
247215 Thread .sleep(1000 )
248216
249- // Click model selection to open dialog
250- device.findObject( UiSelector ().clickable( true ).instance( 2 ))?.click( )
217+ // Click model selection
218+ clickByDescription( " Select Model " )
251219 Thread .sleep(500 )
252220
253- // Verify dialog appears
254- val dialogTitle = device.wait(Until .findObject (By .textContains(" Select model" )), TIMEOUT_MS )
255- assertTrue(" Model selection dialog should appear" , dialogTitle != null )
221+ // Verify model dialog appears
222+ val modelDialog = device.wait(Until .hasObject (By .textContains(" Select model" )), TIMEOUT_MS )
223+ assertTrue(" Model selection dialog should appear" , modelDialog )
256224
257- // Dismiss dialog
258- clickDialogButton (" Cancel" )
225+ // Dismiss
226+ clickButton (" Cancel" )
259227 Thread .sleep(500 )
260228
261229 // Click tokenizer selection
262- device.findObject( UiSelector ().clickable( true ).instance( 3 ))?.click( )
230+ clickByDescription( " Select Tokenizer " )
263231 Thread .sleep(500 )
264232
265233 // Verify tokenizer dialog
266- val tokenizerDialog = device.wait(Until .findObject (By .textContains(" Select tokenizer" )), TIMEOUT_MS )
267- assertTrue(" Tokenizer selection dialog should appear" , tokenizerDialog != null )
234+ val tokenizerDialog = device.wait(Until .hasObject (By .textContains(" Select tokenizer" )), TIMEOUT_MS )
235+ assertTrue(" Tokenizer selection dialog should appear" , tokenizerDialog)
268236
269- clickDialogButton (" Cancel" )
237+ clickButton (" Cancel" )
270238 }
271239
272240 // --- Helper Methods ---
273241
274- private fun dismissInitialDialog () {
275- Thread .sleep(500 )
276- clickDialogButton(" OK" )
277- Thread .sleep(300 )
242+ private fun clickButton (text : String ) {
243+ device.findObject(UiSelector ().text(text))?.click()
244+ ? : device.findObject(UiSelector ().textContains(text))?.click()
278245 }
279246
280- private fun clickDialogButton (buttonText : String ) {
281- val button = device.findObject(UiSelector ().text(buttonText))
282- if (button.exists()) {
283- button.click()
284- } else {
285- // Try case-insensitive
286- device.findObject(UiSelector ().textContains(buttonText))?.click()
287- }
247+ private fun clickByDescription (description : String ) {
248+ device.findObject(UiSelector ().description(description))?.click()
249+ ? : device.findObject(UiSelector ().descriptionContains(description))?.click()
288250 }
289251
290252 private fun loadModel () {
291253 // Go to settings
292- device.findObject(UiSelector ().descriptionContains(" Settings" ))?.click()
293- ? : device.findObject(UiSelector ().clickable(true ).instance(1 ))?.click()
254+ clickByDescription(" Settings" )
294255 Thread .sleep(1000 )
295256
296- // Select model file
297- device.findObject( UiSelector ().clickable( true ).instance( 2 ))?.click( )
257+ // Select model
258+ clickByDescription( " Select Model " )
298259 Thread .sleep(500 )
299260 device.wait(Until .findObject(By .textContains(modelFile)), TIMEOUT_MS )?.click()
300261 Thread .sleep(500 )
301262
302- // Select tokenizer file
303- device.findObject( UiSelector ().clickable( true ).instance( 3 ))?.click( )
263+ // Select tokenizer
264+ clickByDescription( " Select Tokenizer " )
304265 Thread .sleep(500 )
305266 device.wait(Until .findObject(By .textContains(tokenizerFile)), TIMEOUT_MS )?.click()
306267 Thread .sleep(500 )
307268
308- // Click load model
309- device.findObject(UiSelector ().textContains (" Load Model" ))?.click()
269+ // Load model
270+ device.findObject(UiSelector ().text (" Load Model" ))?.click()
310271 Thread .sleep(500 )
311272
312273 // Confirm
313- clickDialogButton (" OK" )
274+ clickButton (" OK" )
314275 Thread .sleep(500 )
315276 }
316277
317278 private fun waitForText (text : String , timeoutMs : Long ): Boolean {
318279 val startTime = System .currentTimeMillis()
319280 while (System .currentTimeMillis() - startTime < timeoutMs) {
320- val element = device.findObject(UiSelector ().textContains(text))
321- if (element.exists()) {
281+ if (device.findObject(UiSelector ().textContains(text)).exists()) {
322282 return true
323283 }
324284 Thread .sleep(500 )
0 commit comments