|
9 | 9 | import software.coley.collections.Unchecked; |
10 | 10 | import software.coley.recaf.info.BasicFileInfo; |
11 | 11 | import software.coley.recaf.info.FileInfo; |
| 12 | +import software.coley.recaf.info.JarFileInfo; |
12 | 13 | import software.coley.recaf.info.JvmClassInfo; |
13 | 14 | import software.coley.recaf.info.Named; |
14 | 15 | import software.coley.recaf.info.StubClassInfo; |
|
22 | 23 | import software.coley.recaf.path.PathNodes; |
23 | 24 | import software.coley.recaf.path.ResourcePathNode; |
24 | 25 | import software.coley.recaf.path.WorkspacePathNode; |
| 26 | +import software.coley.recaf.services.text.TextFormatConfig; |
| 27 | +import software.coley.recaf.services.workspace.io.BasicClassPatcher; |
| 28 | +import software.coley.recaf.services.workspace.io.BasicInfoImporter; |
| 29 | +import software.coley.recaf.services.workspace.io.BasicResourceImporter; |
| 30 | +import software.coley.recaf.services.workspace.io.InfoImporterConfig; |
| 31 | +import software.coley.recaf.services.workspace.io.ResourceImporter; |
| 32 | +import software.coley.recaf.services.workspace.io.ResourceImporterConfig; |
| 33 | +import software.coley.recaf.test.TestClassUtils; |
25 | 34 | import software.coley.recaf.test.dummy.AccessibleFields; |
26 | 35 | import software.coley.recaf.test.dummy.HelloWorld; |
27 | 36 | import software.coley.recaf.test.dummy.StringConsumer; |
28 | 37 | import software.coley.recaf.test.dummy.VariedModifierFields; |
| 38 | +import software.coley.recaf.ui.config.WorkspaceExplorerConfig; |
| 39 | +import software.coley.recaf.util.ZipCreationUtils; |
| 40 | +import software.coley.recaf.util.io.ByteSource; |
| 41 | +import software.coley.recaf.util.io.ByteSources; |
29 | 42 | import software.coley.recaf.workspace.model.BasicWorkspace; |
30 | 43 | import software.coley.recaf.workspace.model.Workspace; |
31 | 44 | import software.coley.recaf.workspace.model.bundle.BasicFileBundle; |
32 | 45 | import software.coley.recaf.workspace.model.bundle.BasicJvmClassBundle; |
33 | 46 | import software.coley.recaf.workspace.model.bundle.JvmClassBundle; |
| 47 | +import software.coley.recaf.workspace.model.bundle.VersionedJvmClassBundle; |
34 | 48 | import software.coley.recaf.workspace.model.resource.WorkspaceFileResource; |
35 | 49 | import software.coley.recaf.workspace.model.resource.WorkspaceFileResourceBuilder; |
36 | 50 | import software.coley.recaf.workspace.model.resource.WorkspaceResource; |
|
39 | 53 | import java.io.IOException; |
40 | 54 | import java.util.ArrayList; |
41 | 55 | import java.util.Collection; |
| 56 | +import java.util.Collections; |
42 | 57 | import java.util.Comparator; |
| 58 | +import java.util.IdentityHashMap; |
43 | 59 | import java.util.List; |
44 | 60 | import java.util.Map; |
45 | 61 | import java.util.Objects; |
| 62 | +import java.util.Set; |
46 | 63 | import java.util.function.Function; |
47 | 64 | import java.util.stream.Stream; |
48 | 65 |
|
|
54 | 71 | * Tests for {@link WorkspaceTreeNode}. |
55 | 72 | */ |
56 | 73 | class WorkspaceTreeNodeTest { |
| 74 | + static ResourceImporter importer; |
| 75 | + // Workspace model for testing - Used by multiple tests, but some will make their own. |
57 | 76 | static Workspace workspace; |
58 | 77 | static WorkspaceResource primaryResource; |
59 | 78 | static WorkspaceResource resourceWithEmbedded; |
@@ -84,6 +103,11 @@ class WorkspaceTreeNodeTest { |
84 | 103 |
|
85 | 104 | @BeforeAll |
86 | 105 | static void setup() throws IOException { |
| 106 | + importer = new BasicResourceImporter( |
| 107 | + new BasicInfoImporter(new InfoImporterConfig(), new TextFormatConfig(), new BasicClassPatcher()), |
| 108 | + new ResourceImporterConfig() |
| 109 | + ); |
| 110 | + |
87 | 111 | BasicFileBundle fileBundle = new BasicFileBundle(); |
88 | 112 |
|
89 | 113 | primaryJvmBundle = fromClasses( |
@@ -349,6 +373,57 @@ void nameOrdering() { |
349 | 373 | }); |
350 | 374 | } |
351 | 375 |
|
| 376 | + /** |
| 377 | + * While not an issue of name overloading in adjacent tree nodes, this test covers a case/regression where we |
| 378 | + * saw multiple classes with the same name in different versioned paths not being all shown. |
| 379 | + * This scenario has the effect of making all {@link VersionedJvmClassBundle} have map equality too which was |
| 380 | + * another issue with an older version of the path node path containment/equality logic. |
| 381 | + */ |
| 382 | + @Test |
| 383 | + void multipleVersionedPaths() throws Exception { |
| 384 | + String classPath = HelloWorld.class.getName().replace(".", "/"); |
| 385 | + String classPackage = classPath.substring(0, classPath.lastIndexOf('/')); |
| 386 | + byte[] classBytes = TestClassUtils.fromRuntimeClass(HelloWorld.class).getBytecode(); |
| 387 | + |
| 388 | + // Create JAR with 'META-INF/versions/<dummyversion>/<dummypackage>/HelloWorld.class' for multiple versions. |
| 389 | + byte[] zipBytes = ZipCreationUtils.builder() |
| 390 | + .add(classPath + ".class", classBytes) |
| 391 | + .add(JarFileInfo.MULTI_RELEASE_PREFIX + "9/" + classPath + ".class", classBytes) |
| 392 | + .add(JarFileInfo.MULTI_RELEASE_PREFIX + "11/" + classPath + ".class", classBytes) |
| 393 | + .add(JarFileInfo.MULTI_RELEASE_PREFIX + "16/" + classPath + ".class", classBytes) |
| 394 | + .add(JarFileInfo.MULTI_RELEASE_PREFIX + "21/" + classPath + ".class", classBytes) |
| 395 | + .add(JarFileInfo.MULTI_RELEASE_PREFIX + "25/" + classPath + ".class", classBytes) |
| 396 | + .bytes(); |
| 397 | + ByteSource zipSource = ByteSources.wrap(zipBytes); |
| 398 | + |
| 399 | + // Build the workspace and validate the versioned bundles exist. |
| 400 | + WorkspaceResource resource = importer.importResource(zipSource); |
| 401 | + BasicWorkspace workspace = new BasicWorkspace(resource); |
| 402 | + assertEquals(5, resource.getVersionedJvmClassBundles().size(), "Expected 5 versioned bundles: 9, 11, 16, 21, 25"); |
| 403 | + |
| 404 | + // Build the tree model. |
| 405 | + WorkspacePathNode rootPath = PathNodes.workspacePath(workspace); |
| 406 | + WorkspaceRootTreeNode root = new WorkspaceRootTreeNode(new WorkspaceExplorerConfig(), rootPath); |
| 407 | + root.build(); |
| 408 | + |
| 409 | + // Iterate over all versions, validate the path to the versioned class exists and that no duplicate tree paths exist. |
| 410 | + final int baseline = -1; |
| 411 | + int[] versions = new int[]{baseline, 9, 11, 16, 21, 25}; |
| 412 | + Set<BundlePathNode> visitedNodes = Collections.newSetFromMap(new IdentityHashMap<>()); |
| 413 | + for (int version : versions) { |
| 414 | + String versionName = version == baseline ? "baseline" : String.valueOf(version); |
| 415 | + ClassPathNode path = version == baseline ? |
| 416 | + workspace.findJvmClass(classPath) : |
| 417 | + workspace.findVersionedJvmClass(classPath, version); |
| 418 | + assertNotNull(path, "Could not find class path for version: " + versionName); |
| 419 | + WorkspaceTreeNode classNode = root.getNodeByPath(path); |
| 420 | + assertNotNull(classNode, "Could not find tree node for class path for version: " + versionName); |
| 421 | + BundlePathNode bundleNode = path.getParent().getParent(); |
| 422 | + assertTrue(visitedNodes.add(bundleNode), "Duplicate bundle node found for version: " + versionName); |
| 423 | + } |
| 424 | + assertEquals(6, visitedNodes.size(), "Expected 6 unique bundle nodes for versions: baseline, 9, 11, 16, 21, 25"); |
| 425 | + } |
| 426 | + |
352 | 427 | /** |
353 | 428 | * The named path sorter was getting confused when checking "does 'a' have 'b' as a parent-directory" if either |
354 | 429 | * String was empty, which is the case for classes in the default package. This led to inconsistent return value |
|
0 commit comments