Skip to content

Commit 48ccc40

Browse files
authored
Implement comprehensive dependency injection with Guice, integrate service layer architecture, add complete testing infrastructure, and enhance CI/CD pipeline for improved maintainability and Folia extensibility (#151)
2 parents b473384 + 19c6a1f commit 48ccc40

28 files changed

+1328
-155
lines changed

.github/workflows/build-pr.yml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,23 @@ jobs:
2222
- name: Setup Gradle
2323
uses: gradle/actions/setup-gradle@v4
2424
- name: Build on ${{ matrix.os }}
25-
run: ./gradlew clean build test
25+
run: ./gradlew clean build test
26+
- name: Publish Test Results
27+
uses: EnricoMi/publish-unit-test-result-action@v2
28+
if: always() && matrix.os == 'ubuntu-latest'
29+
with:
30+
files: "**/build/test-results/test/TEST-*.xml"
31+
check_name: "Test Results"
32+
comment_title: "Unit Test Results"
33+
- name: Upload Test Report
34+
uses: actions/upload-artifact@v4
35+
if: always() && matrix.os == 'ubuntu-latest'
36+
with:
37+
name: test-report
38+
path: build/reports/tests/test/
39+
- name: Upload Coverage Reports
40+
uses: actions/upload-artifact@v4
41+
if: always() && matrix.os == 'ubuntu-latest'
42+
with:
43+
name: coverage-report
44+
path: build/reports/jacoco/test/

build.gradle.kts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ plugins {
99
alias(libs.plugins.paper.yml)
1010
alias(libs.plugins.hangar)
1111
alias(libs.plugins.modrinth)
12+
jacoco
1213
}
1314

1415
if (!File("$rootDir/.git").exists()) {
@@ -59,13 +60,23 @@ dependencies {
5960
implementation(libs.cloud.command.annotations)
6061
implementation(libs.semver)
6162
implementation(libs.adventure.text.feature.pagination)
63+
implementation(libs.guice)
64+
implementation(libs.jakarta.inject)
6265
annotationProcessor(libs.cloud.command.annotations)
6366

6467
implementation(project(":internal-api"))
6568
implementation(project(":WorldGuardv6Support"))
6669
implementation(project(":WorldGuardv7Support"))
6770
implementation(project(":PlotSquaredv6Support"))
6871
implementation(project(":PlotSquaredv7Support"))
72+
73+
// Testing dependencies
74+
testImplementation(libs.junit.jupiter)
75+
testImplementation(libs.mockito.core)
76+
testImplementation(libs.mockito.junit.jupiter)
77+
testImplementation(libs.mockbukkit)
78+
testImplementation(libs.assertj.core)
79+
testRuntimeOnly(libs.junit.platform.launcher)
6980
}
7081

7182
tasks {
@@ -75,6 +86,24 @@ tasks {
7586
named("build") {
7687
dependsOn(shadowJar)
7788
}
89+
test {
90+
useJUnitPlatform()
91+
testLogging {
92+
events("passed", "skipped", "failed")
93+
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
94+
showStandardStreams = false
95+
}
96+
maxParallelForks = 1
97+
98+
// Generate test reports
99+
reports {
100+
junitXml.required.set(true)
101+
html.required.set(true)
102+
}
103+
104+
// Test result publication
105+
finalizedBy(jacocoTestReport)
106+
}
78107
supportedMinecraftVersions.forEach { serverVersion ->
79108
register<RunServer>("run-$serverVersion") {
80109
minecraftVersion(serverVersion)
@@ -91,6 +120,14 @@ tasks {
91120
this.modrinth {
92121
dependsOn(shadowJar)
93122
}
123+
124+
jacocoTestReport {
125+
dependsOn(test)
126+
reports {
127+
xml.required.set(true)
128+
html.required.set(true)
129+
}
130+
}
94131
}
95132

96133

docs/DEPENDENCY_INJECTION.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Dependency Injection Implementation
2+
3+
This document describes the dependency injection (DI) implementation using Google Guice to improve the maintainability and extensibility of AntiRedstoneClock-Remastered.
4+
5+
## Overview
6+
7+
The plugin has been refactored to use dependency injection patterns, making it more modular and easier to test. The implementation uses Google Guice as the DI framework.
8+
9+
## Module Structure
10+
11+
### ServiceModule
12+
Manages core plugin services:
13+
- `RedstoneClockService` - Core redstone clock detection logic
14+
- `UpdateService` - Plugin update checking and notifications
15+
- `CheckTPS` - TPS monitoring functionality
16+
- `TranslationService` - Internationalization support (provider method selects modern/legacy based on server version)
17+
18+
### ExternalSupportModule
19+
Handles optional external plugin integrations:
20+
- `WorldGuardSupport` - Integration with WorldGuard plugin (provider method selects v6/v7 based on version)
21+
- `PlotsquaredSupport` - Integration with PlotSquared plugin (provider method selects v6/v7 based on version)
22+
23+
### CommandModule
24+
Manages command classes:
25+
- `ReloadCommand`
26+
- `DisplayActiveClocksCommand`
27+
- `FeatureCommand`
28+
29+
### ListenerModule
30+
Manages event listeners:
31+
- `PlayerListener`
32+
- `ObserverListener`
33+
- Additional listeners can be added as they get refactored
34+
35+
## Benefits
36+
37+
### 1. Improved Maintainability
38+
- Dependencies are explicitly declared through constructor injection
39+
- No more manual service creation scattered throughout the main class
40+
- Clear separation of concerns between different modules
41+
42+
### 2. Enhanced Testability
43+
- Services can be easily mocked for unit testing
44+
- Dependencies are injected rather than hard-coded
45+
- Easier to create isolated test scenarios
46+
47+
### 3. Better Extensibility for Folia
48+
- Service layer is now modular and can be easily extended
49+
- Platform-specific implementations can be swapped in via configuration
50+
- Clear interface boundaries make it easier to add platform-specific optimizations
51+
52+
### 4. Reduced Coupling
53+
- Classes depend on interfaces rather than concrete implementations
54+
- Easier to change implementations without affecting consumers
55+
- Better adherence to SOLID principles
56+
57+
## Usage Examples
58+
59+
### Injecting Dependencies
60+
```java
61+
@Singleton
62+
public class SomeService {
63+
private final RedstoneClockService clockService;
64+
private final CheckTPS tpsChecker;
65+
66+
@Inject
67+
public SomeService(RedstoneClockService clockService, CheckTPS tpsChecker) {
68+
this.clockService = clockService;
69+
this.tpsChecker = tpsChecker;
70+
}
71+
}
72+
```
73+
74+
### Adding New Services
75+
To add a new service:
76+
1. Create the service class with `@Inject` constructor
77+
2. Add `@Singleton` if it should be a singleton
78+
3. Bind it in the appropriate module's `configure()` method
79+
4. Inject it where needed
80+
81+
### Creating Platform-Specific Implementations
82+
```java
83+
@Provides
84+
@Singleton
85+
public SomeService provideSomeService(AntiRedstoneClockRemastered plugin) {
86+
if (plugin.getServer().getName().equals("Folia")) {
87+
return new FoliaSomeService(plugin);
88+
} else {
89+
return new DefaultSomeService(plugin);
90+
}
91+
}
92+
```
93+
94+
## Migration Notes
95+
96+
- Legacy listeners that haven't been refactored yet still use manual instantiation
97+
- External support (WorldGuard/PlotSquared) now uses provider methods for version detection
98+
- The main plugin class is significantly simplified with most manual setup removed
99+
100+
## Future Improvements
101+
102+
1. Refactor remaining listeners to use DI
103+
2. Add interface abstractions for better testability
104+
3. Consider platform-specific modules for Folia support
105+
4. Add configuration-based service selection

0 commit comments

Comments
 (0)