diff --git a/jadx-core/src/main/java/jadx/core/export/ExportGradleProject.java b/jadx-core/src/main/java/jadx/core/export/ExportGradleProject.java index 8272bf3a0..3850d34ee 100644 --- a/jadx-core/src/main/java/jadx/core/export/ExportGradleProject.java +++ b/jadx-core/src/main/java/jadx/core/export/ExportGradleProject.java @@ -110,7 +110,8 @@ public class ExportGradleProject { Integer versionCode = Integer.valueOf(manifest.getAttribute("android:versionCode")); String versionName = manifest.getAttribute("android:versionName"); Integer minSdk = Integer.valueOf(usesSdk.getAttribute("android:minSdkVersion")); - Integer targetSdk = Integer.valueOf(usesSdk.getAttribute("android:targetSdkVersion")); + String stringTargetSdk = usesSdk.getAttribute("android:targetSdkVersion"); + Integer targetSdk = stringTargetSdk.isEmpty() ? minSdk : Integer.valueOf(stringTargetSdk); String appName = "UNKNOWN"; if (application.hasAttribute("android:label")) { diff --git a/jadx-core/src/test/java/jadx/api/JadxDecompilerTestUtils.java b/jadx-core/src/test/java/jadx/api/JadxDecompilerTestUtils.java new file mode 100644 index 000000000..dd2680694 --- /dev/null +++ b/jadx-core/src/test/java/jadx/api/JadxDecompilerTestUtils.java @@ -0,0 +1,17 @@ +package jadx.api; + +import jadx.core.dex.nodes.RootNode; +import jadx.core.xmlgen.BinaryXMLParser; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class JadxDecompilerTestUtils { + public static JadxDecompiler getMockDecompiler() { + JadxDecompiler decompiler = mock(JadxDecompiler.class); + RootNode rootNode = new RootNode(new JadxArgs()); + when(decompiler.getRoot()).thenReturn(rootNode); + when(decompiler.getBinaryXmlParser()).thenReturn(new BinaryXMLParser(rootNode)); + return decompiler; + } +} diff --git a/jadx-core/src/test/java/jadx/tests/api/ExportGradleTest.java b/jadx-core/src/test/java/jadx/tests/api/ExportGradleTest.java new file mode 100644 index 000000000..71ca7a2b2 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/api/ExportGradleTest.java @@ -0,0 +1,70 @@ +package jadx.tests.api; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.stream.Stream; + +import org.junit.jupiter.api.io.TempDir; + +import jadx.api.ICodeInfo; +import jadx.api.JadxDecompiler; +import jadx.api.JadxDecompilerTestUtils; +import jadx.api.ResourceFile; +import jadx.core.dex.nodes.RootNode; +import jadx.core.export.ExportGradleProject; +import jadx.core.xmlgen.ResContainer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public abstract class ExportGradleTest { + + private static final String MANIFEST_TESTS_DIR = "src/test/manifest"; + + @TempDir + private File exportDir; + + protected ResContainer createResourceContainer(String filename) { + final ResContainer container = mock(ResContainer.class); + ICodeInfo codeInfo = mock(ICodeInfo.class); + when(codeInfo.getCodeStr()).thenReturn(loadFileContent(new File(MANIFEST_TESTS_DIR, filename))); + when(container.getText()).thenReturn(codeInfo); + return container; + } + + private static String loadFileContent(File filePath) { + StringBuilder contentBuilder = new StringBuilder(); + + try (Stream stream = Files.lines(filePath.toPath(), StandardCharsets.UTF_8)) { + stream.forEach(s -> contentBuilder.append(s).append("\n")); + } catch (IOException e) { + fail("Loading file failed: %s", e.getMessage()); + } + return contentBuilder.toString(); + } + + protected void exportGradle(String manifestFilename, String stringsFileName) { + final JadxDecompiler decompiler = JadxDecompilerTestUtils.getMockDecompiler(); + ResourceFile androidManifest = mock(ResourceFile.class); + final ResContainer androidManifestContainer = createResourceContainer(manifestFilename); + when(androidManifest.loadContent()).thenReturn(androidManifestContainer); + final ResContainer strings = createResourceContainer(stringsFileName); + final RootNode root = decompiler.getRoot(); + + final ExportGradleProject export = + new ExportGradleProject(root, exportDir, androidManifest, strings); + export.init(); + assertThat(export.getSrcOutDir().exists()); + assertThat(export.getResOutDir().exists()); + } + + protected String getAppGradleBuild() { + File appBuildGradle = new File(exportDir, "app/build.gradle"); + assertThat(appBuildGradle.exists()); + return loadFileContent(appBuildGradle); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/export/OptionalTargetSdkVersion.java b/jadx-core/src/test/java/jadx/tests/export/OptionalTargetSdkVersion.java new file mode 100644 index 000000000..f4561140f --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/export/OptionalTargetSdkVersion.java @@ -0,0 +1,18 @@ +package jadx.tests.export; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.ExportGradleTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class OptionalTargetSdkVersion extends ExportGradleTest { + + @Test + void test() { + exportGradle("OptionalTargetSdkVersion.xml", "strings.xml"); + + assertThat(getAppGradleBuild()).contains("targetSdkVersion 14"); + } + +} diff --git a/jadx-core/src/test/manifest/OptionalTargetSdkVersion.xml b/jadx-core/src/test/manifest/OptionalTargetSdkVersion.xml new file mode 100644 index 000000000..ff398dc06 --- /dev/null +++ b/jadx-core/src/test/manifest/OptionalTargetSdkVersion.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/jadx-core/src/test/manifest/strings.xml b/jadx-core/src/test/manifest/strings.xml new file mode 100644 index 000000000..25aaeb622 --- /dev/null +++ b/jadx-core/src/test/manifest/strings.xml @@ -0,0 +1,4 @@ + + + JadxTestApp + diff --git a/jadx-core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/jadx-core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 000000000..ca6ee9cea --- /dev/null +++ b/jadx-core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline \ No newline at end of file