Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package org.autojs.autojs.core.pref.fleet

import android.content.Context
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test

/**
* Tests for FleetProfileApplier running on an Android device.
*
* Uses a dedicated test SharedPreferences instance to avoid modifying the app's
* real preferences. Each test cleans up after itself.
*/
class FleetProfileApplierTest {

private val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
private val prefs by lazy {
context.getSharedPreferences("test_fleet_profile", Context.MODE_PRIVATE)
}

@Before
fun setUp() {
prefs.edit().clear().commit()
}

@Test
fun applyJson_appliesBoolean() {
val result = FleetProfileApplier.applyJson(context, """{"foreground_service": true}""", prefs)
assertTrue("expected success", result.success)
assertEquals(1, result.appliedCount)
assertEquals(0, result.skippedCount)
assertEquals(listOf("foreground_service"), result.appliedKeys)
}

@Test
fun applyJson_appliesString() {
val result = FleetProfileApplier.applyJson(context, """{"restart_strategy": "quick"}""", prefs)
assertTrue(result.success)
assertEquals(1, result.appliedCount)
assertEquals(listOf("restart_strategy"), result.appliedKeys)
}

@Test
fun applyJson_appliesMultipleKeys() {
val json = """{
"foreground_service": true,
"floating_menu_shown": false,
"stable_mode": true,
"guard_mode": true
}"""
val result = FleetProfileApplier.applyJson(context, json, prefs)
assertTrue(result.success)
assertEquals(4, result.appliedCount)
assertEquals(4, result.appliedKeys.size)
}

@Test
fun applyJson_acceptsRawKey() {
val d = "${'$'}"
val result = FleetProfileApplier.applyJson(context, """{"key_${d}_foreground_service": true}""", prefs)
assertTrue(result.success)
assertEquals(1, result.appliedCount)
}

@Test
fun applyJson_resolvesValueAlias() {
val result = FleetProfileApplier.applyJson(context, """{"timed_task_backend": "alarm"}""", prefs)
assertTrue("expected success", result.success)
assertEquals(1, result.appliedCount)
}

@Test
fun applyJson_unknownKey_skipped() {
val result = FleetProfileApplier.applyJson(context, """{"nonexistent_key": true}""", prefs)
assertFalse("expected failure", result.success)
assertEquals(0, result.appliedCount)
assertEquals(1, result.skippedCount)
assertEquals(listOf("nonexistent_key"), result.failedKeys)
}

@Test
fun applyJson_invalidJson_returnsError() {
val result = FleetProfileApplier.applyJson(context, """{invalid""", prefs)
assertFalse(result.success)
assertEquals(0, result.appliedCount)
assertTrue(result.errors.first().contains("Invalid JSON"))
}

@Test
fun applyJson_skipsMetaKeys() {
val json = """{
"_meta": {"name": "test"},
"foreground_service": true
}"""
val result = FleetProfileApplier.applyJson(context, json, prefs)
assertTrue(result.success)
assertEquals(1, result.appliedCount)
}

@Test
fun applyJson_clearExisting_clearsBeforeApply() {
FleetProfileApplier.applyJson(context, """{"foreground_service": true}""", prefs)
val result = FleetProfileApplier.applyJson(context, """{
"_meta": {"clear_existing": true},
"stable_mode": true
}""", prefs)
assertTrue(result.success)
assertEquals(1, result.appliedCount)
}

@Test
fun applyJson_appliesInt() {
val result = FleetProfileApplier.applyJson(context, """{"editor_text_size": 18}""", prefs)
assertTrue(result.success)
assertEquals(1, result.appliedCount)
}

@Test
fun applyJson_appliesFloat() {
val result = FleetProfileApplier.applyJson(context, """{"screen_capture_request_delay": 0.5}""", prefs)
assertTrue(result.success)
assertEquals(1, result.appliedCount)
}

@Test
fun applyJson_appliesStringArray() {
val json = """{"file_extensions": ["js", "jsx"]}"""
val result = FleetProfileApplier.applyJson(context, json, prefs)
assertTrue(result.success)
assertEquals(1, result.appliedCount)
}

@Test
fun result_toJson_includesAllFields() {
val result = FleetProfileApplier.Result(
success = true, appliedCount = 2, skippedCount = 0,
appliedKeys = listOf("a", "b"), failedKeys = emptyList(),
errors = emptyList(), message = "ok"
)
val json = result.toJson()
assertTrue(json.getBoolean("success"))
assertEquals(2, json.getInt("applied_count"))
assertEquals(0, json.getInt("skipped_count"))
assertEquals(2, json.getJSONArray("applied_keys").length())
assertEquals("ok", json.getString("message"))
}

@Test
fun result_toLogLine_includesTimestamp() {
val result = FleetProfileApplier.Result(
success = true, appliedCount = 1, skippedCount = 0,
appliedKeys = listOf("x"), failedKeys = emptyList(),
errors = emptyList(), message = "ok"
)
val line = result.toLogLine()
assertTrue("expected timestamp prefix", line.matches(Regex("""\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z .*""")))
assertTrue(line.contains("INFO "))
assertTrue(line.contains("applied=1"))
assertTrue(line.contains("message=\"ok\""))
}

@Test
fun result_toLogLine_levelIsErrorOnFail() {
val result = FleetProfileApplier.Result(
success = false, appliedCount = 0, skippedCount = 1,
appliedKeys = emptyList(), failedKeys = listOf("bad"),
errors = listOf("Unknown key: bad"), message = "fail"
)
val line = result.toLogLine()
assertTrue("expected ERROR level", line.contains("ERROR"))
assertTrue(line.contains("failed=bad"))
}

@Test
fun result_toLogLine_levelIsWarnOnPartial() {
val result = FleetProfileApplier.Result(
success = false, appliedCount = 2, skippedCount = 1,
appliedKeys = listOf("a", "b"), failedKeys = listOf("c"),
errors = listOf("Unknown key: c"), message = "partial"
)
val line = result.toLogLine()
assertTrue("expected WARN level on partial", line.contains("WARN "))
}

@Test
fun applyFromPath_parsesFile() {
val file = java.io.File(context.cacheDir, "test-fleet-profile.json")
file.writeText("""{"foreground_service": true}""", Charsets.UTF_8)
val result = FleetProfileApplier.applyFromPath(context, file.absolutePath)
assertTrue(result.success)
assertEquals(1, result.appliedCount)
file.delete()
}

@Test
fun applyFromPath_missingFile_returnsError() {
val result = FleetProfileApplier.applyFromPath(context, "/nonexistent/path.json")
assertFalse(result.success)
assertTrue(result.message.contains("Failed to read"))
}
}
20 changes: 20 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,26 @@
</intent-filter>
</activity>

<activity
android:name="org.autojs.autojs.core.pref.fleet.FleetProfileActivity"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay"
tools:ignore="ExportedActivity">

<intent-filter>
<action android:name="org.autojs.autojs6.action.APPLY_FLEET_PROFILE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="application/json" />
</intent-filter>
</activity>

<service android:name="org.autojs.autojs.ui.enhancedfloaty.FloatyService" />

<service
Expand Down
31 changes: 31 additions & 0 deletions app/src/main/assets/fleet_profile_default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"_meta": {
"name": "AutoJs6 fleet/headless default profile",
"description": "Recommended defaults for unattended watchdog deployments (Termux + Shizuku + wireless ADB).",
"version": 1,
"clear_existing": false
},
"foreground_service": true,
"floating_menu_shown": false,
"enable_a11y_service_with_secure_settings": true,
"enable_a11y_service_with_root_access": false,
"stable_mode": true,
"guard_mode": true,
"use_volume_control_running": false,
"use_volume_control_record": false,
"record_toast": true,
"extending_js_build_in_objects": false,
"rhino_java_primitive_wrap": false,
"auto_check_for_updates": false,
"restart_strategy": "quick",
"scheduled_restart_backend": "alarm_manager",
"timed_task_backend": "alarm",
"night_mode": "follow_system",
"file_extensions": "show_all",
"hidden_files": "show",
"keep_screen_on_when_in_foreground": "off",
"display_over_other_apps": false,
"post_notifications_permission": true,
"all_files_access": false,
"root_mode": "force_non_root"
}
Loading