From 9095be1ea1af86c331a6dbb15a64eb1c7a99a05c Mon Sep 17 00:00:00 2001 From: Siddartha Pothapragada Date: Fri, 24 Apr 2026 15:20:52 -0700 Subject: [PATCH] Android: add Module lifecycle and API coverage tests Add 13 new tests covering previously untested Module public APIs: - Load modes: MMAP, FILE - getMethods(): verify returns array containing "forward" - getMethodMetadata(): verify name and backends - readLogBuffer() / readLogBufferStatic(): verify non-null - etdump(): verify callable - Destroyed-state throws for getMethods, getMethodMetadata, readLogBuffer, etdump - Double destroy is safe (idempotent) This commit was authored with the help of Claude. --- .../executorch/ModuleInstrumentationTest.kt | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/extension/android/executorch_android/src/androidTest/java/org/pytorch/executorch/ModuleInstrumentationTest.kt b/extension/android/executorch_android/src/androidTest/java/org/pytorch/executorch/ModuleInstrumentationTest.kt index eb2b6f096a1..c0f9a34d4a1 100644 --- a/extension/android/executorch_android/src/androidTest/java/org/pytorch/executorch/ModuleInstrumentationTest.kt +++ b/extension/android/executorch_android/src/androidTest/java/org/pytorch/executorch/ModuleInstrumentationTest.kt @@ -171,6 +171,138 @@ class ModuleInstrumentationTest { module.destroy() } + // --- Load mode tests --- + + @Test + @Throws(IOException::class) + fun testLoadWithMmapMode() { + val module = Module.load(getTestFilePath(TEST_FILE_NAME), Module.LOAD_MODE_MMAP) + try { + val results = module.forward(EValue.from(dummyInput())) + Assert.assertTrue(results[0].isTensor) + } finally { + module.destroy() + } + } + + @Test + @Throws(IOException::class) + fun testLoadWithFileMode() { + val module = Module.load(getTestFilePath(TEST_FILE_NAME), Module.LOAD_MODE_FILE) + try { + val results = module.forward(EValue.from(dummyInput())) + Assert.assertTrue(results[0].isTensor) + } finally { + module.destroy() + } + } + + // --- getMethods / getMethodMetadata tests --- + + @Test + @Throws(IOException::class) + fun testGetMethods() { + val module = Module.load(getTestFilePath(TEST_FILE_NAME)) + try { + val methods = module.getMethods() + Assert.assertNotNull(methods) + Assert.assertTrue(methods.contains(FORWARD_METHOD)) + } finally { + module.destroy() + } + } + + @Test + @Throws(IOException::class) + fun testGetMethodMetadata() { + val module = Module.load(getTestFilePath(TEST_FILE_NAME)) + try { + val metadata = module.getMethodMetadata(FORWARD_METHOD) + Assert.assertNotNull(metadata) + Assert.assertEquals(FORWARD_METHOD, metadata.name) + Assert.assertNotNull(metadata.backends) + } finally { + module.destroy() + } + } + + // --- Log buffer tests --- + + @Test + @Throws(IOException::class) + fun testReadLogBuffer() { + val module = Module.load(getTestFilePath(TEST_FILE_NAME)) + try { + val logs = module.readLogBuffer() + Assert.assertNotNull(logs) + } finally { + module.destroy() + } + } + + @Test + fun testReadLogBufferStatic() { + val logs = Module.readLogBufferStatic() + Assert.assertNotNull(logs) + } + + // --- etdump test --- + + @Test + @Throws(IOException::class) + fun testEtdump() { + val module = Module.load(getTestFilePath(TEST_FILE_NAME)) + try { + module.etdump() + } finally { + module.destroy() + } + } + + // --- Destroyed-state tests for remaining methods --- + + @Test + @Throws(IOException::class) + fun testGetMethodsOnDestroyedModule() { + val module = Module.load(getTestFilePath(TEST_FILE_NAME)) + module.destroy() + Assert.assertThrows(IllegalStateException::class.java) { module.getMethods() } + } + + @Test + @Throws(IOException::class) + fun testGetMethodMetadataOnDestroyedModule() { + val module = Module.load(getTestFilePath(TEST_FILE_NAME)) + module.destroy() + Assert.assertThrows(IllegalStateException::class.java) { + module.getMethodMetadata(FORWARD_METHOD) + } + } + + @Test + @Throws(IOException::class) + fun testReadLogBufferOnDestroyedModule() { + val module = Module.load(getTestFilePath(TEST_FILE_NAME)) + module.destroy() + Assert.assertThrows(IllegalStateException::class.java) { module.readLogBuffer() } + } + + @Test + @Throws(IOException::class) + fun testEtdumpOnDestroyedModule() { + val module = Module.load(getTestFilePath(TEST_FILE_NAME)) + module.destroy() + Assert.assertThrows(IllegalStateException::class.java) { module.etdump() } + } + + @Test + @Throws(IOException::class) + fun testDoubleDestroyIsSafe() { + val module = Module.load(getTestFilePath(TEST_FILE_NAME)) + module.destroy() + module.destroy() + } + companion object { private const val TEST_FILE_NAME = "/mobilenet_v2.pte" private const val MISSING_FILE_NAME = "/missing.pte"