Back to Repositories

Testing DEX Input Processing Implementation in skylot/jadx

This test suite validates the DexInputPlugin functionality for processing and analyzing DEX and APK files in the JADX decompiler project. It tests core capabilities for loading different types of Android executable files and extracting their structural information.

Test Coverage Overview

The test suite provides comprehensive coverage of DEX file processing capabilities.

Key areas tested include:
  • APK file loading and processing
  • Raw DEX file handling
  • Smali code compilation and analysis
  • Class structure extraction
  • Method and field inspection
  • Access flags verification

Implementation Analysis

The testing approach utilizes JUnit 5 framework with focused test methods for different input types. The implementation employs systematic verification of DEX processing pipeline, using both real and synthetic test files.

Notable patterns include:
  • Resource-based test file loading
  • Structured class visitor pattern
  • Atomic counters for validation
  • Automated cleanup with try-with-resources

Technical Details

Testing infrastructure includes:
  • JUnit Jupiter test framework
  • AssertJ assertions library
  • SmaliTestUtils for compilation
  • Custom ICodeLoader interface
  • DexInputPlugin processing engine
  • Path-based file handling

Best Practices Demonstrated

The test suite exemplifies high-quality testing practices through systematic validation of core functionality.

Notable practices include:
  • Isolated test cases for different input types
  • Proper resource management
  • Comprehensive class structure verification
  • Performance timing measurements
  • Detailed logging for debugging
  • Assertion-based validation

skylot/jadx

jadx-plugins/jadx-dex-input/src/test/java/jadx/plugins/input/dex/DexInputPluginTest.java

            
package jadx.plugins.input.dex;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.jupiter.api.Test;

import jadx.api.plugins.input.ICodeLoader;
import jadx.api.plugins.input.data.AccessFlags;
import jadx.api.plugins.input.data.AccessFlagsScope;
import jadx.api.plugins.input.data.ICodeReader;
import jadx.plugins.input.dex.utils.SmaliTestUtils;

import static org.assertj.core.api.Assertions.assertThat;

class DexInputPluginTest {

	@Test
	public void loadSampleApk() throws Exception {
		processFile(Paths.get(ClassLoader.getSystemResource("samples/app-with-fake-dex.apk").toURI()));
	}

	@Test
	public void loadHelloWorld() throws Exception {
		processFile(Paths.get(ClassLoader.getSystemResource("samples/hello.dex").toURI()));
	}

	@Test
	public void loadTestSmali() throws Exception {
		processFile(SmaliTestUtils.compileSmaliFromResource("samples/test.smali"));
	}

	private static void processFile(Path sample) throws IOException {
		System.out.println("Input file: " + sample.toAbsolutePath());
		long start = System.currentTimeMillis();
		List<Path> files = Collections.singletonList(sample);
		try (ICodeLoader result = new DexInputPlugin().loadFiles(files)) {
			AtomicInteger count = new AtomicInteger();
			result.visitClasses(cls -> {
				System.out.println();
				System.out.println("Class: " + cls.getType());
				System.out.println("AccessFlags: " + AccessFlags.format(cls.getAccessFlags(), AccessFlagsScope.CLASS));
				System.out.println("SuperType: " + cls.getSuperType());
				System.out.println("Interfaces: " + cls.getInterfacesTypes());
				System.out.println("Attributes: " + cls.getAttributes());
				count.getAndIncrement();

				cls.visitFieldsAndMethods(
						System.out::println,
						mth -> {
							System.out.println("---");
							System.out.println(mth);
							ICodeReader codeReader = mth.getCodeReader();
							if (codeReader != null) {
								codeReader.visitInstructions(insn -> {
									insn.decode();
									System.out.println(insn);
								});
							}
							System.out.println("---");
							System.out.println(mth.disassembleMethod());
							System.out.println("---");
						});
				System.out.println("----");
				System.out.println(cls.getDisassembledCode());
				System.out.println("----");
			});
			assertThat(count.get()).isGreaterThan(0);
		}
		System.out.println("Time: " + (System.currentTimeMillis() - start) + "ms");
	}
}