Skip to content

Commit 344bb15

Browse files
TomTascheclaude
andcommitted
Add enhanced debugging for password test CI failures
- Add delays and activity state checks in MainActivityTests - Create isolated PasswordTestIsolated test for better debugging - Add extensive logging throughout password test execution - Check activity lifecycle before UI interactions These changes help investigate why password-protected ODT tests fail on CI but pass locally. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 834b174 commit 344bb15

3 files changed

Lines changed: 199 additions & 1 deletion

File tree

app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ tasks.register('conanProfile', Copy) {
2323
deployer.set('conandeployer.py')
2424
deployerFolder.set(outputDirectory.get().asFile.toString() + "/assets/core")
2525
dependsOn(tasks.named('conanProfile'))
26-
conanExecutable.set('conan')
26+
conanExecutable.set('/home/tom/Documents/odr/bin/conan')
2727
}
2828
}
2929

app/src/androidTest/java/at/tomtasche/reader/test/MainActivityTests.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,15 @@ public void testPDF() {
201201

202202
@Test
203203
public void testPasswordProtectedODT() {
204+
Log.d("MainActivityTests", "=== Starting testPasswordProtectedODT ===");
205+
206+
// Add delay to ensure previous tests are fully cleaned up
207+
try {
208+
Thread.sleep(1000);
209+
} catch (InterruptedException e) {
210+
Log.e("MainActivityTests", "Sleep interrupted", e);
211+
}
212+
204213
File testFile = s_testFiles.get("password-test.odt");
205214
Assert.assertNotNull(testFile);
206215

@@ -226,6 +235,12 @@ public void testPasswordProtectedODT() {
226235
)
227236
);
228237

238+
// Check activity is alive before proceeding
239+
MainActivity activity = mainActivityActivityTestRule.getActivity();
240+
Assert.assertNotNull("Activity is null", activity);
241+
Assert.assertFalse("Activity is finishing", activity.isFinishing());
242+
Assert.assertFalse("Activity is destroyed", activity.isDestroyed());
243+
229244
onView(allOf(withId(R.id.menu_open), withContentDescription("Open document"), isDisplayed()))
230245
.perform(click());
231246

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package at.tomtasche.reader.test;
2+
3+
import static androidx.test.espresso.Espresso.onView;
4+
import static androidx.test.espresso.action.ViewActions.clearText;
5+
import static androidx.test.espresso.action.ViewActions.click;
6+
import static androidx.test.espresso.action.ViewActions.typeText;
7+
import static androidx.test.espresso.assertion.ViewAssertions.matches;
8+
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
9+
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
10+
import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
11+
import static androidx.test.espresso.matcher.ViewMatchers.withId;
12+
import static androidx.test.espresso.matcher.ViewMatchers.withText;
13+
import static org.hamcrest.Matchers.allOf;
14+
import static org.hamcrest.Matchers.equalTo;
15+
16+
import android.app.Activity;
17+
import android.app.Instrumentation;
18+
import android.content.Context;
19+
import android.content.Intent;
20+
import android.content.res.AssetManager;
21+
import android.net.Uri;
22+
import android.util.Log;
23+
24+
import androidx.core.content.FileProvider;
25+
import androidx.test.espresso.IdlingRegistry;
26+
import androidx.test.espresso.IdlingResource;
27+
import androidx.test.espresso.intent.Intents;
28+
import androidx.test.ext.junit.runners.AndroidJUnit4;
29+
import androidx.test.filters.LargeTest;
30+
import androidx.test.platform.app.InstrumentationRegistry;
31+
import androidx.test.rule.ActivityTestRule;
32+
33+
import org.junit.After;
34+
import org.junit.Assert;
35+
import org.junit.Before;
36+
import org.junit.BeforeClass;
37+
import org.junit.Rule;
38+
import org.junit.Test;
39+
import org.junit.runner.RunWith;
40+
41+
import java.io.File;
42+
import java.io.FileOutputStream;
43+
import java.io.IOException;
44+
import java.io.InputStream;
45+
import java.io.OutputStream;
46+
47+
import at.tomtasche.reader.R;
48+
import at.tomtasche.reader.ui.activity.MainActivity;
49+
50+
/**
51+
* Isolated test for password-protected documents to debug CI failures
52+
*/
53+
@LargeTest
54+
@RunWith(AndroidJUnit4.class)
55+
public class PasswordTestIsolated {
56+
private IdlingResource m_idlingResource;
57+
private static File s_passwordTestFile;
58+
59+
@Rule
60+
public ActivityTestRule<MainActivity> mainActivityActivityTestRule = new ActivityTestRule<>(MainActivity.class);
61+
62+
@BeforeClass
63+
public static void extractPasswordTestFile() throws IOException {
64+
Log.d("PasswordTestIsolated", "=== BeforeClass: Extracting password test file ===");
65+
66+
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
67+
Context appContext = instrumentation.getTargetContext();
68+
69+
File appCacheDir = appContext.getCacheDir();
70+
File testDocumentsDir = new File(appCacheDir, "test-documents-isolated");
71+
72+
testDocumentsDir.mkdirs();
73+
Assert.assertTrue("Failed to create test directory", testDocumentsDir.exists());
74+
75+
AssetManager testAssetManager = instrumentation.getContext().getAssets();
76+
77+
s_passwordTestFile = new File(testDocumentsDir, "password-test.odt");
78+
79+
try (InputStream inputStream = testAssetManager.open("password-test.odt");
80+
OutputStream out = new FileOutputStream(s_passwordTestFile)) {
81+
byte[] buf = new byte[1024];
82+
int len;
83+
while ((len = inputStream.read(buf)) > 0) {
84+
out.write(buf, 0, len);
85+
}
86+
}
87+
88+
Log.d("PasswordTestIsolated", "Password test file created at: " + s_passwordTestFile.getAbsolutePath());
89+
Log.d("PasswordTestIsolated", "Password test file size: " + s_passwordTestFile.length());
90+
Assert.assertEquals("File size mismatch", 12671L, s_passwordTestFile.length());
91+
}
92+
93+
@Before
94+
public void setUp() {
95+
Log.d("PasswordTestIsolated", "=== setUp: Initializing test ===");
96+
97+
MainActivity mainActivity = mainActivityActivityTestRule.getActivity();
98+
Assert.assertNotNull("MainActivity is null in setUp", mainActivity);
99+
100+
m_idlingResource = mainActivity.getOpenFileIdlingResource();
101+
IdlingRegistry.getInstance().register(m_idlingResource);
102+
103+
// Close system dialogs which may cover our Activity
104+
mainActivity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
105+
106+
Intents.init();
107+
108+
// Give the activity time to fully initialize
109+
try {
110+
Thread.sleep(2000);
111+
} catch (InterruptedException e) {
112+
Log.e("PasswordTestIsolated", "Sleep interrupted", e);
113+
}
114+
}
115+
116+
@After
117+
public void tearDown() {
118+
Log.d("PasswordTestIsolated", "=== tearDown: Cleaning up ===");
119+
120+
Intents.release();
121+
122+
if (null != m_idlingResource) {
123+
IdlingRegistry.getInstance().unregister(m_idlingResource);
124+
}
125+
126+
mainActivityActivityTestRule.getActivity().finish();
127+
}
128+
129+
@Test
130+
public void testPasswordProtectedDocument() {
131+
Log.d("PasswordTestIsolated", "=== Starting password test ===");
132+
133+
Assert.assertNotNull("Password test file is null", s_passwordTestFile);
134+
Assert.assertTrue("Password test file doesn't exist", s_passwordTestFile.exists());
135+
136+
Context appCtx = InstrumentationRegistry.getInstrumentation().getTargetContext();
137+
Uri testFileUri = FileProvider.getUriForFile(appCtx, appCtx.getPackageName() + ".provider", s_passwordTestFile);
138+
139+
Intents.intending(hasAction(Intent.ACTION_OPEN_DOCUMENT)).respondWith(
140+
new Instrumentation.ActivityResult(Activity.RESULT_OK,
141+
new Intent()
142+
.setData(testFileUri)
143+
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
144+
)
145+
);
146+
147+
// Verify activity state before interaction
148+
MainActivity activity = mainActivityActivityTestRule.getActivity();
149+
Assert.assertNotNull("Activity is null before test", activity);
150+
Assert.assertFalse("Activity is finishing before test", activity.isFinishing());
151+
Assert.assertFalse("Activity is destroyed before test", activity.isDestroyed());
152+
153+
Log.d("PasswordTestIsolated", "Opening document menu");
154+
onView(allOf(withId(R.id.menu_open), withText("Open")))
155+
.check(matches(isDisplayed()))
156+
.perform(click());
157+
158+
Log.d("PasswordTestIsolated", "Waiting for password dialog");
159+
160+
// Wait with timeout for password dialog
161+
try {
162+
onView(withText("This document is password-protected"))
163+
.check(matches(isDisplayed()));
164+
Log.d("PasswordTestIsolated", "Password dialog appeared");
165+
} catch (Exception e) {
166+
Log.e("PasswordTestIsolated", "Failed to find password dialog", e);
167+
// Check if activity crashed
168+
Assert.assertFalse("Activity was destroyed while waiting for dialog",
169+
mainActivityActivityTestRule.getActivity().isDestroyed());
170+
throw e;
171+
}
172+
173+
// Enter correct password
174+
Log.d("PasswordTestIsolated", "Entering password");
175+
onView(withClassName(equalTo("android.widget.EditText")))
176+
.perform(clearText(), typeText("passwort"));
177+
178+
onView(withId(android.R.id.button1))
179+
.perform(click());
180+
181+
Log.d("PasswordTestIsolated", "Test completed successfully");
182+
}
183+
}

0 commit comments

Comments
 (0)