Skip to content

Commit c00bd5a

Browse files
committed
test: cover incoming SAF content intents
1 parent 1593fdb commit c00bd5a

5 files changed

Lines changed: 143 additions & 4 deletions

File tree

app/src/androidTest/java/com/kyhsgeekcode/disassembler/DocumentIntentFixtures.kt

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,47 @@ import kotlinx.serialization.json.Json
2020

2121
private const val FILE_PROVIDER_AUTHORITY = "com.kyhsgeekcode.disassembler.provider"
2222

23-
fun createOpenDocumentResult(
23+
fun createIncomingContentUri(
2424
displayName: String,
2525
content: ByteArray
26-
): Instrumentation.ActivityResult {
26+
): Uri {
2727
val context = ApplicationProvider.getApplicationContext<Context>()
2828
val inputFile = context.filesDir.resolve("androidTest/input/$displayName")
2929
inputFile.parentFile?.mkdirs()
3030
inputFile.writeBytes(content)
31-
val uri = testFileUri(context, inputFile)
31+
return testFileUri(context, inputFile)
32+
}
33+
34+
fun createActionViewIntent(
35+
displayName: String,
36+
content: ByteArray
37+
): Intent {
38+
val uri = createIncomingContentUri(displayName, content)
39+
return Intent(Intent.ACTION_VIEW).apply {
40+
data = uri
41+
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
42+
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
43+
}
44+
}
45+
46+
fun createExtraStreamIntent(
47+
displayName: String,
48+
content: ByteArray
49+
): Intent {
50+
val uri = createIncomingContentUri(displayName, content)
51+
return Intent(Intent.ACTION_SEND).apply {
52+
type = "*/*"
53+
putExtra(Intent.EXTRA_STREAM, uri)
54+
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
55+
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
56+
}
57+
}
58+
59+
fun createOpenDocumentResult(
60+
displayName: String,
61+
content: ByteArray
62+
): Instrumentation.ActivityResult {
63+
val uri = createIncomingContentUri(displayName, content)
3264
val resultIntent = Intent()
3365
.setData(uri)
3466
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.kyhsgeekcode.disassembler
2+
3+
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
4+
import androidx.compose.ui.test.onAllNodesWithTag
5+
import androidx.compose.ui.test.onNodeWithTag
6+
import androidx.test.ext.junit.runners.AndroidJUnit4
7+
import androidx.test.ext.junit.rules.ActivityScenarioRule
8+
import com.kyhsgeekcode.disassembler.ui.MainTestTags
9+
import org.junit.Rule
10+
import org.junit.Test
11+
import org.junit.rules.RuleChain
12+
import org.junit.runner.RunWith
13+
14+
@RunWith(AndroidJUnit4::class)
15+
class MainActivityActionViewIntentFlowTest {
16+
companion object {
17+
private const val PROJECT_OPEN_TIMEOUT_MS = 10_000L
18+
}
19+
20+
private val projectCleanupRule = ProjectStateCleanupRule()
21+
private val activityRule = ActivityScenarioRule<MainActivity>(
22+
createActionViewIntent(
23+
displayName = "incoming-view.apk",
24+
content = "incoming-view-content".encodeToByteArray()
25+
)
26+
)
27+
private val composeRule = AndroidComposeTestRule<ActivityScenarioRule<MainActivity>, MainActivity>(
28+
activityRule
29+
) { rule: ActivityScenarioRule<MainActivity> ->
30+
var activity: MainActivity? = null
31+
rule.scenario.onActivity { activity = it }
32+
checkNotNull(activity)
33+
}
34+
35+
@get:Rule
36+
val rules: RuleChain = RuleChain.outerRule(projectCleanupRule)
37+
.around(composeRule)
38+
39+
@Test
40+
fun actionViewContentUri_opensProjectAndSurvivesRecreate() {
41+
waitForProjectOpen()
42+
43+
composeRule.activityRule.scenario.recreate()
44+
45+
waitForProjectOpen()
46+
composeRule.onNodeWithTag(MainTestTags.EXPORT_PROJECT_BUTTON).assertExists()
47+
}
48+
49+
private fun waitForProjectOpen() {
50+
composeRule.waitUntil(timeoutMillis = PROJECT_OPEN_TIMEOUT_MS) {
51+
composeRule.onAllNodesWithTag(MainTestTags.EXPORT_PROJECT_BUTTON)
52+
.fetchSemanticsNodes().isNotEmpty()
53+
}
54+
}
55+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.kyhsgeekcode.disassembler
2+
3+
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
4+
import androidx.compose.ui.test.onAllNodesWithTag
5+
import androidx.compose.ui.test.onNodeWithTag
6+
import androidx.test.ext.junit.runners.AndroidJUnit4
7+
import androidx.test.ext.junit.rules.ActivityScenarioRule
8+
import com.kyhsgeekcode.disassembler.ui.MainTestTags
9+
import org.junit.Rule
10+
import org.junit.Test
11+
import org.junit.rules.RuleChain
12+
import org.junit.runner.RunWith
13+
14+
@RunWith(AndroidJUnit4::class)
15+
class MainActivityExtraStreamIntentFlowTest {
16+
companion object {
17+
private const val PROJECT_OPEN_TIMEOUT_MS = 10_000L
18+
}
19+
20+
private val projectCleanupRule = ProjectStateCleanupRule()
21+
private val activityRule = ActivityScenarioRule<MainActivity>(
22+
createExtraStreamIntent(
23+
displayName = "incoming-stream.apk",
24+
content = "incoming-stream-content".encodeToByteArray()
25+
)
26+
)
27+
private val composeRule = AndroidComposeTestRule<ActivityScenarioRule<MainActivity>, MainActivity>(
28+
activityRule
29+
) { rule: ActivityScenarioRule<MainActivity> ->
30+
var activity: MainActivity? = null
31+
rule.scenario.onActivity { activity = it }
32+
checkNotNull(activity)
33+
}
34+
35+
@get:Rule
36+
val rules: RuleChain = RuleChain.outerRule(projectCleanupRule)
37+
.around(composeRule)
38+
39+
@Test
40+
fun extraStreamContentUri_opensProject() {
41+
waitForProjectOpen()
42+
composeRule.onNodeWithTag(MainTestTags.EXPORT_PROJECT_BUTTON).assertExists()
43+
}
44+
45+
private fun waitForProjectOpen() {
46+
composeRule.waitUntil(timeoutMillis = PROJECT_OPEN_TIMEOUT_MS) {
47+
composeRule.onAllNodesWithTag(MainTestTags.EXPORT_PROJECT_BUTTON)
48+
.fetchSemanticsNodes().isNotEmpty()
49+
}
50+
}
51+
}

docs/maintenance/backlog-triage.ko.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
| 작업 묶음 | 관련 이슈 | 제안 상태 | 판단 | 다음 액션 |
3535
| --- | --- | --- | --- | --- |
36-
| 최신 Android storage 정책 | `#95` | `planned-fast-follow` | 핵심 유지보수 항목이며 이미 SAF 전환을 시작했다 | 앱 전체 import/open 경로를 SAF 중심으로 계속 이관 |
36+
| 최신 Android storage 정책 | `#95` | `planned-fast-follow` | 핵심 유지보수 항목이며 이미 SAF 전환과 incoming `ACTION_VIEW`/`EXTRA_STREAM` instrumentation 회귀 검증을 시작했다 | 앱 전체 import/open 경로를 SAF 중심으로 계속 이관하고 남은 실기기 경계를 확인 |
3737
| 릴리스 산출물 부재 | `#719` | `planned-fast-follow` | 코드 문제보다 릴리스 파이프라인 문제다 | CI artifact, preview prerelease, formal release 흐름으로 운영 정리 |
3838
| 대용량/메모리/RecyclerView 크래시 | `#219`, `#235`, `#442`, `#523` | `planned-fast-follow` | `#728`에서 큰 파일 byte cache 제한과 문자열 검색 결과 상한/stable key를 먼저 넣었다 | `#728` 병합 후 실제 150MB 파일과 긴 문자열 리스트로 재검증하고 나머지 OOM 경로를 분리 |
3939
| `.so`/ELF/autosetup | `#514`, `#543`, `#576`, `#137` | `planned-fast-follow` | `#728`에서 64-bit ELF machine type 매핑과 override autosetup 재적용 경로를 먼저 수정했다 | 실제 `.so` 샘플로 재검증하고 남는 parser 문제만 분리 |

docs/maintenance/implementation-log.ko.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
| 이슈 `#719` 최신판 체험 경로 부족 | rolling `Preview latest` prerelease를 발행하는 `preview.yml`을 추가하고 README에 preview/debug 배포 정책을 문서화 | CI artifact만 아는 사람만 최신판을 받던 상태에서, GitHub Releases의 prerelease 자산으로도 최신 debug 빌드를 쉽게 배포할 수 있게 했다 | `.github/workflows/preview.yml`, `README.md` | 완료 |
1616
| 저장소 정책 현대화 착수, 이슈 `#95` 대응 기반 | SAF 기반 선택 경로를 도입하고 `content://` 입력을 앱 내부 import 파일로 저장 | 외부 절대경로 전제를 줄이고, 재시작 이후에도 다시 열 수 있는 URI 권한 흐름을 시작했다 | `app/src/main/java/com/kyhsgeekcode/filechooser/NewFileChooserActivity.kt`, `app/src/main/java/com/kyhsgeekcode/disassembler/viewmodel/MainViewModel.kt`, `app/src/main/java/com/kyhsgeekcode/disassembler/PermissionUtils.kt` | 진행 중 |
1717
| 이슈 `#95` 외부 문서 인텐트 권한 누락 | 앱이 `ACTION_VIEW``EXTRA_STREAM`으로 열린 경우에도 persistable grant 가능 여부를 계산하고 `content://` URI 권한을 선제적으로 유지 | SAF picker 밖에서 들어온 문서도 같은 storage 정책 흐름으로 흡수해서, provider가 허용하는 경우 앱 재실행 이후에도 접근이 끊길 가능성을 줄였다 | `app/src/main/java/com/kyhsgeekcode/disassembler/MainActivity.kt`, `app/src/main/java/com/kyhsgeekcode/disassembler/PermissionUtils.kt`, `app/src/test/java/com/kyhsgeekcode/disassembler/PermissionUtilsTest.kt` | 완료 |
18+
| 이슈 `#95` incoming content intent 회귀 검증 부재 | `ACTION_VIEW``EXTRA_STREAM`으로 들어오는 `content://` 문서를 직접 주입하는 instrumentation fixture와 flow test를 추가했다 | picker 기반 SAF import만 보던 기존 merge gate를 넓혀서, 외부 앱에서 전달된 문서가 실제 프로젝트로 열리고 activity recreate 뒤에도 UI가 살아 있는지 CI에서 계속 감시할 수 있게 했다 | `app/src/androidTest/java/com/kyhsgeekcode/disassembler/DocumentIntentFixtures.kt`, `app/src/androidTest/java/com/kyhsgeekcode/disassembler/MainActivityActionViewIntentFlowTest.kt`, `app/src/androidTest/java/com/kyhsgeekcode/disassembler/MainActivityExtraStreamIntentFlowTest.kt` | 완료 |
1819
| 이슈 `#95` legacy 저장소 권한 범위 축소 | import entry-point별 legacy 권한 요구를 분리하고, `Advanced import`만 Android 9 이하에서 권한 요청을 하도록 변경 | 기본 SAF import는 어떤 지원 SDK에서도 저장소 권한을 요구하지 않게 고정하고, 구형 Android의 raw filesystem 진입점에서만 legacy 권한 모델을 제한적으로 유지했다 | `app/src/main/java/com/kyhsgeekcode/disassembler/importing/ImportEntryPointCatalog.kt`, `app/src/main/java/com/kyhsgeekcode/disassembler/ui/MainTab.kt`, `app/src/test/java/com/kyhsgeekcode/disassembler/importing/ImportEntryPointCatalogTest.kt` | 완료 |
1920
| 프로젝트 경계의 raw path 의존 | `ProjectModel`에 source helper를 추가하고 프로젝트 트리/리스트/저장소가 helper를 사용하도록 교체 | `sourceFilePath` 문자열 조합을 한곳으로 모아서 이후 `sourceDescriptor` 중심 구조로 더 옮기기 쉬운 상태를 만들었다 | `app/src/main/java/com/kyhsgeekcode/disassembler/project/models/ProjectModel.kt`, `app/src/main/java/com/kyhsgeekcode/disassembler/ui/FileDrawerTree.kt`, `app/src/main/java/com/kyhsgeekcode/disassembler/FileDrawerListAdapter.kt`, `app/src/main/java/com/kyhsgeekcode/disassembler/FileDrawerListItem.kt`, `app/src/main/java/com/kyhsgeekcode/disassembler/project/ProjectDataStorage.kt`, `app/src/main/java/com/kyhsgeekcode/disassembler/viewmodel/MainViewModel.kt`, `app/src/test/java/com/kyhsgeekcode/disassembler/ProjectManagerTest.kt` | 완료 |
2021
| broad storage 우회 경로 정리 | 더 이상 쓰지 않는 legacy picker 코드와 Android 11+의 `MANAGE_ALL_FILES_ACCESS` 유도 설정을 제거 | SAF 기반 경로를 기본 흐름으로 고정하고, 정책상 불리한 all-files access 진입점을 줄였다 | `app/src/main/java/com/kyhsgeekcode/disassembler/MainActivity.kt`, `app/src/main/java/com/kyhsgeekcode/disassembler/preference/SettingsFragment.kt`, `app/src/main/res/xml/pref_settings.xml`, `app/src/main/res/xml-v30/pref_settings.xml`, `app/src/main/res/values/array.xml` | 완료 |

0 commit comments

Comments
 (0)