Files
rea/vitest.config.ts
Quentin Limo 96fd98cce0 feat(apktool): decode Android resource tables through a caller-supplied launcher (#1556)
* feat(apktool): decode Android resource tables through a caller-supplied launcher

Add an Apktool family with two operations over the caller's apktool
launcher (REA_APKTOOL_COMMAND, absolute, or apktool on PATH):

- inspect_apktool_client: resolved launcher, selection source, and
  version; decodes nothing.
- decode_android_resources: apktool d --no-src in a provider-owned
  workspace that is removed before the result returns. Projects the
  decoded AndroidManifest.xml (bounded at 512 KiB), apktool.yml version
  and SDK facts exactly as printed, string resources verbatim (default or
  one locale, bounded at 2000 entries, nested-markup entries counted as
  unparsed), available string locales, and the decoded file count with a
  hard cap. The target APK is digested with SHA-256 before decoding so
  Evidence binds the observation to the exact input.

Smali production is skipped by design: Java-source recovery belongs to
the JADX family. Workspace cleanup happens before results return; an
unremovable workspace is named in the limitations instead of abandoned.

Verified against apktool 2.7.0-dirty (Debian packaging) on Linux with
OpenJDK 25 against a signed aapt2-built probe APK with two resource
locales: launcher version, on-disk digest agreement, metadata projection,
manifest package agreement, and both string tables (default and de).
Parser goldens come from the same decode. READMEs carry the new
'Android resources' row in every supported language.

* fix(apktool): own snapshots and bound decoded resource reads

* docs(apktool): describe owned resources and bounded projection

---------

Co-authored-by: REA Contributor <contributor@rea.tools>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-10 23:04:12 +08:00

174 lines
4.8 KiB
TypeScript

import { realpathSync } from "node:fs";
import { availableParallelism, tmpdir } from "node:os";
import { join } from "node:path";
import { defineConfig } from "vitest/config";
const CANONICAL_TEMPORARY_DIRECTORY = realpathSync(tmpdir());
const COVERAGE_ENABLED = process.argv.some((argument) =>
argument.startsWith("--coverage"),
);
const COVERAGE_SHARD = process.argv.some((argument) =>
argument.startsWith("--shard="),
);
// Keep local and CI concurrency bounded by the runner rather than command locks.
const MAX_TEST_WORKERS = Math.min(2, availableParallelism());
const TEST_PROJECTS = [
{
name: "domain",
include: ["src/{contracts,domain}/**/*.test.ts"],
pool: "threads" as const,
maxWorkers: MAX_TEST_WORKERS,
// Domain and contract modules are pure and own no process-global state, so
// per-file module isolation adds startup cost without protecting a seam.
isolate: false,
},
{
name: "services",
include: ["src/application/**/*.test.ts"],
pool: "threads" as const,
maxWorkers: MAX_TEST_WORKERS,
// Service tests use recording ports and own no process-global state. Share
// their module graph so file startup does not dominate the lane.
isolate: false,
},
{
name: "adapters",
include: [
"src/*.test.ts",
"src/{apktool,jeb,adb,artifacts,browser,dotnet,evm,firmware,ghidra,hopper,ida,inspector,javascript,native,process,reference,server,windows}/**/*.test.ts",
],
pool: "forks" as const,
maxWorkers: MAX_TEST_WORKERS,
},
{
name: "composition",
include: ["tests/composition/**/*.test.ts"],
pool: "threads" as const,
maxWorkers: MAX_TEST_WORKERS,
},
{
name: "boundary",
include: ["tests/boundary/**/*.test.ts"],
exclude: [
"tests/boundary/mcp/**/*.test.ts",
"tests/boundary/process/**/*.test.ts",
],
pool: "forks" as const,
maxWorkers: MAX_TEST_WORKERS,
},
{
// Process-tree observations share host sampling resources; keep this small
// lane serial while the independent projects run together.
name: "process-boundary",
include: ["tests/boundary/process/**/*.test.ts"],
pool: "forks" as const,
maxWorkers: MAX_TEST_WORKERS,
fileParallelism: false,
sequence: { groupOrder: 1 },
},
{
name: "mcp-boundary",
include: ["tests/boundary/mcp/**/*.test.ts"],
pool: "threads" as const,
maxWorkers: MAX_TEST_WORKERS,
// Each file creates and closes an explicit in-memory MCP session. Reuse the
// immutable server graph so module startup does not outweigh transport work.
isolate: false,
},
{
name: "acceptance",
include: ["tests/acceptance/**/*.test.ts"],
pool: "forks" as const,
maxWorkers: MAX_TEST_WORKERS,
},
{
name: "process-global",
include: ["tests/process-global/**/*.test.ts"],
pool: "forks" as const,
maxWorkers: MAX_TEST_WORKERS,
},
{
name: "conformance",
include: ["tests/conformance/**/*.test.ts"],
pool: "threads" as const,
maxWorkers: MAX_TEST_WORKERS,
},
{
name: "evaluation",
include: ["tests/evaluation/**/*.test.ts"],
pool: "threads" as const,
maxWorkers: MAX_TEST_WORKERS,
},
];
const ZERO_COVERAGE_THRESHOLDS = {
statements: 0,
branches: 0,
functions: 0,
lines: 0,
"src/domain/**": {
statements: 0,
branches: 0,
functions: 0,
lines: 0,
},
"src/contracts/**": {
statements: 0,
branches: 0,
functions: 0,
lines: 0,
},
};
const projects = TEST_PROJECTS.map((project) => ({
extends: true as const,
test: project,
}));
export default defineConfig({
test: {
env: { TMPDIR: CANONICAL_TEMPORARY_DIRECTORY },
maxWorkers: MAX_TEST_WORKERS,
projects,
retry: 0,
reporters: ["default"],
runner: "./tests/processOwnershipRunner.ts",
// Boundary projects may compete with TypeScript, docs, and package checks
// under Turbo. Keep the deadline bounded while avoiding false failures from
// host-level CPU and filesystem contention.
testTimeout: COVERAGE_ENABLED ? 60_000 : 30_000,
coverage: {
enabled: false,
provider: "v8",
reportsDirectory: join(
tmpdir(),
`rea-vitest-coverage-${String(process.pid)}`,
),
include: ["src/**"],
thresholds: COVERAGE_SHARD
? ZERO_COVERAGE_THRESHOLDS
: {
statements: 65,
branches: 60,
functions: 60,
lines: 68,
"src/domain/**": {
statements: 80,
branches: 75,
functions: 75,
lines: 80,
},
"src/contracts/**": {
statements: 85,
branches: 80,
functions: 80,
lines: 85,
},
},
reporter: ["text", "text-summary"],
},
},
});