Skip to content

Commit

Permalink
Merge(FabricMC#894): create a clean patch for issue resolution and co…
Browse files Browse the repository at this point in the history
…rresponding tests
  • Loading branch information
DavidCWQ committed Mar 3, 2024
1 parent d69cb72 commit 982f3e4
Show file tree
Hide file tree
Showing 9 changed files with 298 additions and 1 deletion.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ dependencies {
// Unit testing for mod metadata
testImplementation('org.junit.jupiter:junit-jupiter:5.9.2')
testRuntimeOnly('org.junit.platform:junit-platform-launcher')

// Unit testing for logging mod that have providers
testImplementation("org.mockito:mockito-core:5.10.0")
}

apply from: rootProject.file('gradle/installer-json.gradle')
Expand Down
44 changes: 43 additions & 1 deletion src/main/java/net/fabricmc/loader/impl/FabricLoaderImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import java.util.function.Consumer;
import java.util.stream.Collectors;

import com.google.common.annotations.VisibleForTesting;
import org.objectweb.asm.Opcodes;

import net.fabricmc.accesswidener.AccessWidener;
Expand Down Expand Up @@ -229,6 +230,7 @@ private void setup() throws ModResolutionException {
modCandidates = ModResolver.resolve(modCandidates, getEnvironmentType(), envDisabledMods);

dumpModList(modCandidates);
dumpModsHavingProvider(modCandidates);

Path cacheDir = gameDir.resolve(CACHE_DIR_NAME);
Path outputdir = cacheDir.resolve(PROCESSED_MODS_DIR_NAME);
Expand Down Expand Up @@ -282,6 +284,46 @@ private void setup() throws ModResolutionException {
modCandidates = null;
}

// loaded mods are the subset of fabric mods.
@VisibleForTesting
public void dumpModsHavingProvider(List<ModCandidate> LoadedMods) {
StringBuilder logText = new StringBuilder(); // List of mods having provider

List<ModCandidate> subLevelMods = LoadedMods.stream()
.filter(mod -> !mod.getParentMods().isEmpty())
.collect(Collectors.toList());

int subLevelModsCount = subLevelMods.size();
for (ModCandidate subLevelMod : subLevelMods) {
boolean hasParent = !subLevelMod.getParentMods().isEmpty();
ModCandidate parentMod = subLevelMod;
while (hasParent) {
List<ModCandidate> parentMods = new ArrayList<>(parentMod.getParentMods());
// Each nested mod has only one parent due to tree structure.
parentMod = parentMods.get(0);
// Update hasParent to see if parent has parent
hasParent = !parentMod.getParentMods().isEmpty();
}

if (logText.length() > 0) logText.append(System.lineSeparator());

logText.append("\t-");
logText.append(' ').append(subLevelMod.getId());
logText.append(' ').append(subLevelMod.getVersion().getFriendlyString());
logText.append(" (in ");
logText.append(' ').append(parentMod.getId());
logText.append(' ').append(parentMod.getVersion().getFriendlyString());
logText.append(')');

}

Log.info(LogCategory.GENERAL, "Found %d loaded mod%s that ha%s providers:%n%s",
subLevelModsCount,
subLevelModsCount != 1 ? "s" : "",
subLevelModsCount != 1 ? "ve" : "s",
logText);
}

private void dumpModList(List<ModCandidate> mods) {
StringBuilder modListText = new StringBuilder();

Expand All @@ -304,7 +346,7 @@ private void dumpModList(List<ModCandidate> mods) {
}

private void dumpModList0(ModCandidate mod, StringBuilder log, int nestLevel, boolean[] lastItemOfNestLevel) {
if (log.length() > 0) log.append('\n');
if (log.length() > 0) log.append(System.lineSeparator());

for (int depth = 0; depth < nestLevel; depth++) {
log.append(depth == 0 ? "\t" : lastItemOfNestLevel[depth] ? " " : " | ");
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/net/fabricmc/loader/impl/discovery/ModCandidate.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;

import com.google.common.annotations.VisibleForTesting;

import net.fabricmc.loader.api.Version;
import net.fabricmc.loader.api.metadata.ModDependency;
import net.fabricmc.loader.impl.game.GameProvider.BuiltinMod;
Expand Down Expand Up @@ -84,6 +86,11 @@ static long hash(ZipEntry entry) {
return entry.getCrc() << 32 | entry.getSize();
}

@VisibleForTesting
public static ModCandidate createTestData(List<Path> paths, String localPath, long hash, LoaderModMetadata metadata, boolean requiresRemap, Collection<ModCandidate> nestedMods) {
return new ModCandidate(paths, localPath, hash, metadata, requiresRemap, nestedMods);
}

private static long getSize(long hash) {
return hash & 0xffffffffL;
}
Expand Down Expand Up @@ -183,6 +190,11 @@ boolean addParent(ModCandidate parent) {
return true;
}

@VisibleForTesting
public void testAddParent(ModCandidate parent) {
addParent(parent);
}

public int getMinNestLevel() {
return minNestLevel;
}
Expand Down
145 changes: 145 additions & 0 deletions src/test/java/net/fabricmc/test/LogProvidedModsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* Copyright 2016 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.test;

import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import net.fabricmc.loader.impl.discovery.ModCandidate;
import net.fabricmc.loader.impl.metadata.DependencyOverrides;
import net.fabricmc.loader.impl.metadata.LoaderModMetadata;
import net.fabricmc.loader.impl.metadata.ModMetadataParser;
import net.fabricmc.loader.impl.metadata.ParseMetadataException;
import net.fabricmc.loader.impl.metadata.VersionOverrides;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import net.fabricmc.loader.impl.FabricLoaderImpl;
import net.fabricmc.loader.impl.util.log.Log;
import net.fabricmc.loader.impl.util.log.LogHandler;

public class LogProvidedModsTest {
private LogHandler logHandler;
private static Path metadataPath;
@BeforeAll
public static void setupPaths() {
Path testLocation = new File(System.getProperty("user.dir")).toPath()
.resolve("src")
.resolve("test")
.resolve("resources")
.resolve("testing");
metadataPath = testLocation.resolve("metadata");
}

private static LoaderModMetadata parseMetadata(Path path) throws ParseMetadataException, IOException {
try (InputStream is = Files.newInputStream(path)) {
return ModMetadataParser.parseMetadata(is, "null", Collections.emptyList(),
new VersionOverrides(),
new DependencyOverrides(Paths.get("null")), false);
}
}

/*
* Setup log handler before each test.
*/
@BeforeEach
public void setUp() {
// Create a mock of the class that contains function log
logHandler = mock(LogHandler.class);
Mockito.when(logHandler.shouldLog(Mockito.any(), Mockito.any())).thenReturn(true);
Mockito.doNothing().when(logHandler).log(Mockito.anyLong(), Mockito.any(), Mockito.any(),
Mockito.any(), Mockito.any(), Mockito.anyBoolean(), Mockito.anyBoolean());
Log.init(logHandler);
}

/*
* Test that the log handler is called with the correct message when there are
* nested mods with providers found.
*/
@Test
@DisplayName("Log nested mods that have providers")
public void testModsHavingProvider() throws ParseMetadataException, IOException {

LoaderModMetadata metadata1 = parseMetadata(metadataPath.resolve("fabric.test.mod1.json"));
LoaderModMetadata metadata2 = parseMetadata(metadataPath.resolve("fabric.test.mod2.json"));
LoaderModMetadata metadata3 = parseMetadata(metadataPath.resolve("fabric.test.mod3.json"));
LoaderModMetadata metadata4 = parseMetadata(metadataPath.resolve("fabric.test.mod4.json"));
LoaderModMetadata metadata5 = parseMetadata(metadataPath.resolve("fabric.test.mod5.json"));

// Create nested testing mods
List<ModCandidate> LoadedMods;

{
ModCandidate testMode1 = ModCandidate.createTestData(null, null,
-1, metadata1, false, Collections.emptyList());
Collection<ModCandidate> nestedMod2 = new ArrayList<>();
nestedMod2.add(testMode1);
ModCandidate testMode2 = ModCandidate.createTestData(null, null,
-1, metadata2, false, nestedMod2);
for (ModCandidate child : testMode2.getNestedMods()) {
child.testAddParent(testMode2);
}
ModCandidate testMode3 = ModCandidate.createTestData(null, null,
-1, metadata3, false, Collections.emptyList());
Collection<ModCandidate> nestedMod4 = new ArrayList<>();
nestedMod4.add(testMode2);
nestedMod4.add(testMode3);
ModCandidate testMode4 = ModCandidate.createTestData(null, null,
-1, metadata4, false, nestedMod4);
for (ModCandidate child : testMode4.getNestedMods()) {
child.testAddParent(testMode4);
}
ModCandidate testMode5 = ModCandidate.createTestData(null, null,
-1, metadata5, false, Collections.emptyList());

LoadedMods = new ArrayList<>();
LoadedMods.add(testMode1);
LoadedMods.add(testMode2);
LoadedMods.add(testMode3);
LoadedMods.add(testMode4);
LoadedMods.add(testMode5);
}

// Call as an INSTANCE rather than reflection
FabricLoaderImpl.INSTANCE.dumpModsHavingProvider(LoadedMods);

String expectedLog = "Found 3 loaded mods that have providers:"
+ System.lineSeparator() + "\t- mod1 0.1.0 (in mod4 1.1.1)"
+ System.lineSeparator() + "\t- mod2 2.2.7 (in mod4 1.1.1)"
+ System.lineSeparator() + "\t- mod3 9.1.1 (in mod4 1.1.1)";

Mockito.verify(logHandler, Mockito.times(1)).log(
Mockito.anyLong(), Mockito.any(), Mockito.any(),
eq(expectedLog),
Mockito.any(), Mockito.anyBoolean(), Mockito.anyBoolean());
}
}
19 changes: 19 additions & 0 deletions src/test/resources/testing/metadata/fabric.test.mod1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"schemaVersion": 1,
"id": "mod1",
"name": "Test Mod1",
"version": "0.1.0",
"entrypoints": {
"preLaunch": [
"net.fabricmc.test.TestMod"
],
"main": [
"net.fabricmc.test.TestMod"
],
"test:testing": [
"net.fabricmc.test.EntrypointTest::instanceEntry",
"net.fabricmc.test.EntrypointTest::staticEntry",
"net.fabricmc.test.EntrypointTest::FIELD_ENTRY"
]
}
}
19 changes: 19 additions & 0 deletions src/test/resources/testing/metadata/fabric.test.mod2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"schemaVersion": 1,
"id": "mod2",
"name": "Test Mod2",
"version": "2.2.7",
"entrypoints": {
"preLaunch": [
"net.fabricmc.test.TestMod"
],
"main": [
"net.fabricmc.test.TestMod"
],
"test:testing": [
"net.fabricmc.test.EntrypointTest::instanceEntry",
"net.fabricmc.test.EntrypointTest::staticEntry",
"net.fabricmc.test.EntrypointTest::FIELD_ENTRY"
]
}
}
19 changes: 19 additions & 0 deletions src/test/resources/testing/metadata/fabric.test.mod3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"schemaVersion": 1,
"id": "mod3",
"name": "Test Mod3",
"version": "9.1.1",
"entrypoints": {
"preLaunch": [
"net.fabricmc.test.TestMod"
],
"main": [
"net.fabricmc.test.TestMod"
],
"test:testing": [
"net.fabricmc.test.EntrypointTest::instanceEntry",
"net.fabricmc.test.EntrypointTest::staticEntry",
"net.fabricmc.test.EntrypointTest::FIELD_ENTRY"
]
}
}
19 changes: 19 additions & 0 deletions src/test/resources/testing/metadata/fabric.test.mod4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"schemaVersion": 1,
"id": "mod4",
"name": "Test Mod4",
"version": "1.1.1",
"entrypoints": {
"preLaunch": [
"net.fabricmc.test.TestMod"
],
"main": [
"net.fabricmc.test.TestMod"
],
"test:testing": [
"net.fabricmc.test.EntrypointTest::instanceEntry",
"net.fabricmc.test.EntrypointTest::staticEntry",
"net.fabricmc.test.EntrypointTest::FIELD_ENTRY"
]
}
}
19 changes: 19 additions & 0 deletions src/test/resources/testing/metadata/fabric.test.mod5.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"schemaVersion": 1,
"id": "mod5",
"name": "Test Mod5",
"version": "0.0.0",
"entrypoints": {
"preLaunch": [
"net.fabricmc.test.TestMod"
],
"main": [
"net.fabricmc.test.TestMod"
],
"test:testing": [
"net.fabricmc.test.EntrypointTest::instanceEntry",
"net.fabricmc.test.EntrypointTest::staticEntry",
"net.fabricmc.test.EntrypointTest::FIELD_ENTRY"
]
}
}

0 comments on commit 982f3e4

Please sign in to comment.