//! Patching AndroidManifest.xml to point at the exported icon.
use icon_studio::manifest::{self, Change};
const DIOGRAMOS: &str = r#"<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:label="@string/app_name"
android:allowBackup="false"
android:theme="@style/Theme.Diogramos"
android:usesCleartextTraffic="true">
<activity android:name=".MainActivity" android:exported="true"/>
</application>
</manifest>
"#;
#[test]
fn adds_the_icon_when_the_manifest_has_none() {
// The case that started this: files exported correctly, no icon shown,
// because nothing referenced them.
let (out, change) = manifest::patch_source(DIOGRAMOS, "ic_icon", true).unwrap();
assert_eq!(change, Change::Added);
assert!(out.contains(r#"android:icon="@mipmap/ic_icon""#), "{out}");
assert!(
out.contains(r#"android:roundIcon="@mipmap/ic_icon""#),
"{out}"
);
// Everything else survives.
assert!(out.contains(r#"android:theme="@style/Theme.Diogramos""#));
assert!(out.contains("android.permission.INTERNET"));
assert!(out.contains(".MainActivity"));
}
#[test]
fn inserted_attributes_match_the_surrounding_indentation() {
let (out, _) = manifest::patch_source(DIOGRAMOS, "ic_icon", true).unwrap();
let line = out
.lines()
.find(|l| l.contains("android:icon="))
.expect("icon line");
assert!(
line.starts_with(" android:icon="),
"should line up with the other attributes, got {line:?}"
);
}
#[test]
fn patching_twice_changes_nothing_the_second_time() {
let (once, first) = manifest::patch_source(DIOGRAMOS, "ic_icon", true).unwrap();
assert_eq!(first, Change::Added);
let (twice, second) = manifest::patch_source(&once, "ic_icon", true).unwrap();
assert_eq!(second, Change::AlreadyCorrect);
assert_eq!(once, twice, "idempotent");
}
#[test]
fn repoints_an_icon_that_names_something_else() {
let src = DIOGRAMOS.replace(
r#" android:label="@string/app_name""#,
" android:label=\"@string/app_name\"\n android:icon=\"@mipmap/ic_launcher\"",
);
let (out, change) = manifest::patch_source(&src, "ic_icon", false).unwrap();
assert_eq!(change, Change::Updated);
assert!(out.contains(r#"android:icon="@mipmap/ic_icon""#));
assert!(!out.contains("ic_launcher"));
}
#[test]
fn icon_and_round_icon_are_told_apart() {
// `android:icon` is a substring of `android:roundIcon`; a naive search
// would rewrite the wrong one.
let src = DIOGRAMOS.replace(
r#" android:label="@string/app_name""#,
" android:label=\"@string/app_name\"\n android:roundIcon=\"@mipmap/old_round\"",
);
let (out, _) = manifest::patch_source(&src, "ic_icon", true).unwrap();
assert!(
out.contains(r#"android:roundIcon="@mipmap/ic_icon""#),
"{out}"
);
assert!(out.contains(r#"android:icon="@mipmap/ic_icon""#), "{out}");
assert!(!out.contains("old_round"));
assert_eq!(out.matches("android:roundIcon=").count(), 1);
assert_eq!(out.matches(r#"android:icon="#).count(), 1);
}
#[test]
fn a_greater_than_inside_an_attribute_does_not_end_the_tag() {
let src = r#"<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:label="a > b" android:theme="@style/T">
</application>
</manifest>
"#;
let (out, change) = manifest::patch_source(src, "ic_icon", false).unwrap();
assert_eq!(change, Change::Added);
assert!(out.contains(r#"android:label="a > b""#), "{out}");
assert!(out.contains(r#"android:icon="@mipmap/ic_icon""#), "{out}");
}
#[test]
fn a_single_line_application_tag_is_handled() {
let src = r#"<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:label="@string/app_name">
</application>
</manifest>
"#;
let (out, change) = manifest::patch_source(src, "ic_icon", false).unwrap();
assert_eq!(change, Change::Added);
assert!(out.contains(r#"android:icon="@mipmap/ic_icon""#), "{out}");
}
#[test]
fn a_manifest_without_an_application_element_is_an_error_not_a_guess() {
let src = r#"<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
"#;
assert!(manifest::patch_source(src, "ic_icon", false).is_err());
}
#[test]
fn the_real_cbzauth_manifest_is_already_correct() {
// Ground truth: a manifest that works. Patching it must be a no-op.
let p = "/home/gmorell/Development/projects/cbz_clients_decompilation/recreated/cbzauth/app/src/main/AndroidManifest.xml";
let Ok(src) = std::fs::read_to_string(p) else {
return; // not checked out; nothing to assert
};
let (out, change) = manifest::patch_source(&src, "ic_icon", true).unwrap();
assert_eq!(change, Change::AlreadyCorrect);
assert_eq!(out, src, "a correct manifest must not be rewritten");
}
neiam
/icon-studio
public · Issues · Pulls · Labels · Forks · Compare · Actions failure · Badges · Packages
⭐
Log in to mark this repository.
5.0 KiB · text
History
aca7335