Testing Android Resource XML Generation in JADX Decompiler
This test suite validates the XML resource generation functionality in JADX, focusing on handling various Android resource types including attributes, styles, strings and arrays. The tests ensure proper XML formatting and resource value processing.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
skylot/jadx
jadx-core/src/test/java/jadx/core/xmlgen/ResXmlGenTest.java
package jadx.core.xmlgen;
import java.util.ArrayList;
import java.util.List;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jadx.api.JadxArgs;
import jadx.api.security.IJadxSecurity;
import jadx.api.security.JadxSecurityFlag;
import jadx.api.security.impl.JadxSecurity;
import jadx.core.xmlgen.entry.RawNamedValue;
import jadx.core.xmlgen.entry.RawValue;
import jadx.core.xmlgen.entry.ResourceEntry;
import jadx.core.xmlgen.entry.ValuesParser;
import static org.assertj.core.api.Assertions.assertThat;
class ResXmlGenTest {
private final JadxArgs args = new JadxArgs();
private final IJadxSecurity security = new JadxSecurity(JadxSecurityFlag.all());
private final ManifestAttributes manifestAttributes = new ManifestAttributes(security);
@BeforeEach
void init() {
args.setCodeNewLineStr("\n");
}
@Test
void testSimpleAttr() {
ResourceStorage resStorage = new ResourceStorage(security);
ResourceEntry re = new ResourceEntry(2130903103, "jadx.gui.app", "attr", "size", "");
re.setNamedValues(Lists.list(new RawNamedValue(16777216, new RawValue(16, 64))));
resStorage.add(re);
ValuesParser vp = new ValuesParser(null, resStorage.getResourcesNames());
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);
List<ResContainer> files = resXmlGen.makeResourcesXml(args);
assertThat(files).hasSize(1);
assertThat(files.get(0).getName()).isEqualTo("res/values/attrs.xml");
String input = files.get(0).getText().toString();
assertThat(input).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<resources>\n"
+ " <attr name=\"size\" format=\"dimension\">\n"
+ " </attr>\n"
+ "</resources>");
}
@Test
void testAttrEnum() {
ResourceStorage resStorage = new ResourceStorage(security);
ResourceEntry re = new ResourceEntry(2130903103, "jadx.gui.app", "attr", "size", "");
re.setNamedValues(
Lists.list(
new RawNamedValue(0x01000000, new RawValue(16, 65536)),
new RawNamedValue(0x01040000, new RawValue(16, 1))));
resStorage.add(re);
ValuesParser vp = new ValuesParser(null, resStorage.getResourcesNames());
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);
List<ResContainer> files = resXmlGen.makeResourcesXml(args);
assertThat(files).hasSize(1);
assertThat(files.get(0).getName()).isEqualTo("res/values/attrs.xml");
String input = files.get(0).getText().toString();
assertThat(input).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<resources>\n"
+ " <attr name=\"size\">\n"
+ " <enum name=\"android:string.cancel\" value=\"1\" />\n"
+ " </attr>\n"
+ "</resources>");
}
@Test
void testAttrFlag() {
ResourceStorage resStorage = new ResourceStorage(security);
ResourceEntry re = new ResourceEntry(2130903103, "jadx.gui.app", "attr", "size", "");
re.setNamedValues(
Lists.list(
new RawNamedValue(0x01000000, new RawValue(16, 131072)),
new RawNamedValue(0x01040000, new RawValue(16, 1))));
resStorage.add(re);
ValuesParser vp = new ValuesParser(null, resStorage.getResourcesNames());
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);
List<ResContainer> files = resXmlGen.makeResourcesXml(args);
assertThat(files).hasSize(1);
assertThat(files.get(0).getName()).isEqualTo("res/values/attrs.xml");
String input = files.get(0).getText().toString();
assertThat(input).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<resources>\n"
+ " <attr name=\"size\">\n"
+ " <flag name=\"android:string.cancel\" value=\"1\" />\n"
+ " </attr>\n"
+ "</resources>");
}
@Test
void testAttrMin() {
ResourceStorage resStorage = new ResourceStorage(security);
ResourceEntry re = new ResourceEntry(2130903103, "jadx.gui.app", "attr", "size", "");
re.setNamedValues(
Lists.list(new RawNamedValue(16777216, new RawValue(16, 4)), new RawNamedValue(16777217, new RawValue(16, 1))));
resStorage.add(re);
ValuesParser vp = new ValuesParser(null, resStorage.getResourcesNames());
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);
List<ResContainer> files = resXmlGen.makeResourcesXml(args);
assertThat(files).hasSize(1);
assertThat(files.get(0).getName()).isEqualTo("res/values/attrs.xml");
String input = files.get(0).getText().toString();
assertThat(input).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<resources>\n"
+ " <attr name=\"size\" format=\"integer\" min=\"1\">\n"
+ " </attr>\n"
+ "</resources>");
}
@Test
void testStyle() {
ResourceStorage resStorage = new ResourceStorage(security);
ResourceEntry re = new ResourceEntry(2130903103, "jadx.gui.app", "style", "JadxGui", "");
re.setNamedValues(Lists.list(new RawNamedValue(16842836, new RawValue(1, 17170445))));
resStorage.add(re);
re = new ResourceEntry(2130903104, "jadx.gui.app", "style", "JadxGui.Dialog", "");
re.setParentRef(2130903103);
re.setNamedValues(new ArrayList<>());
resStorage.add(re);
ValuesParser vp = new ValuesParser(null, resStorage.getResourcesNames());
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);
List<ResContainer> files = resXmlGen.makeResourcesXml(args);
assertThat(files).hasSize(1);
assertThat(files.get(0).getName()).isEqualTo("res/values/styles.xml");
String input = files.get(0).getText().toString();
assertThat(input).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<resources>\n"
+ " <style name=\"JadxGui\" parent=\"\">\n"
+ " <item name=\"android:windowBackground\">@android:color/transparent</item>\n"
+ " </style>\n"
+ " <style name=\"JadxGui.Dialog\" parent=\"@style/JadxGui\">\n"
+ " </style>\n"
+ "</resources>");
}
@Test
void testString() {
ResourceStorage resStorage = new ResourceStorage(security);
ResourceEntry re = new ResourceEntry(2130903103, "jadx.gui.app", "string", "app_name", "");
re.setSimpleValue(new RawValue(3, 0));
re.setNamedValues(Lists.list());
resStorage.add(re);
BinaryXMLStrings strings = new BinaryXMLStrings();
strings.put(0, "Jadx Decompiler App");
ValuesParser vp = new ValuesParser(strings, resStorage.getResourcesNames());
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);
List<ResContainer> files = resXmlGen.makeResourcesXml(args);
assertThat(files).hasSize(1);
assertThat(files.get(0).getName()).isEqualTo("res/values/strings.xml");
String input = files.get(0).getText().toString();
assertThat(input).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<resources>\n"
+ " <string name=\"app_name\">Jadx Decompiler App</string>\n"
+ "</resources>");
}
@Test
void testStringFormattedFalse() {
ResourceStorage resStorage = new ResourceStorage(security);
ResourceEntry re = new ResourceEntry(2130903103, "jadx.gui.app", "string", "app_name", "");
re.setSimpleValue(new RawValue(3, 0));
re.setNamedValues(Lists.list());
resStorage.add(re);
BinaryXMLStrings strings = new BinaryXMLStrings();
strings.put(0, "%s at %s");
ValuesParser vp = new ValuesParser(strings, resStorage.getResourcesNames());
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);
List<ResContainer> files = resXmlGen.makeResourcesXml(args);
assertThat(files).hasSize(1);
assertThat(files.get(0).getName()).isEqualTo("res/values/strings.xml");
String input = files.get(0).getText().toString();
assertThat(input).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<resources>\n"
+ " <string name=\"app_name\" formatted=\"false\">%s at %s</string>\n"
+ "</resources>");
}
@Test
void testArrayEscape() {
ResourceStorage resStorage = new ResourceStorage(security);
ResourceEntry re = new ResourceEntry(2130903103, "jadx.gui.app", "array", "single_quote_escape_sample", "");
re.setNamedValues(
Lists.list(new RawNamedValue(16777216, new RawValue(3, 0))));
resStorage.add(re);
BinaryXMLStrings strings = new BinaryXMLStrings();
strings.put(0, "Let's go");
ValuesParser vp = new ValuesParser(strings, resStorage.getResourcesNames());
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);
List<ResContainer> files = resXmlGen.makeResourcesXml(args);
assertThat(files).hasSize(1);
assertThat(files.get(0).getName()).isEqualTo("res/values/arrays.xml");
String input = files.get(0).getText().toString();
assertThat(input).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<resources>\n"
+ " <array name=\"single_quote_escape_sample\">\n"
+ " <item>Let\\'s go</item>\n"
+ " </array>\n"
+ "</resources>");
}
}