Skip to content

Commit 5a30ab0

Browse files
committed
Claude Code tire-kicking: generate some unit tests
1 parent c375665 commit 5a30ab0

7 files changed

Lines changed: 1025 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
Dynmap is a dynamic web mapping plugin/mod for Minecraft servers. It's a multi-platform project supporting Spigot/PaperMC, Forge, and Fabric across multiple Minecraft versions (1.12.2 - 1.21.x).
8+
9+
## Build Commands
10+
11+
```bash
12+
# Build all platforms (requires JDK 21 as default)
13+
./gradlew setup build
14+
15+
# Build outputs go to /target directory
16+
17+
# Build specific module (for faster iteration, but NOT for PR submissions)
18+
./gradlew :fabric-1.18:build
19+
20+
# Forge 1.12.2 (requires JDK 8 - set JAVA_HOME accordingly)
21+
cd oldgradle
22+
./gradlew setup build
23+
```
24+
25+
**JDK Requirements:**
26+
- Default: JDK 21
27+
- Forge 1.12.2 (oldgradle): JDK 8 strictly required
28+
- Runtime targets: JDK 8 (1.16-), JDK 16 (1.17.x), JDK 17 (1.18-1.20.4), JDK 21 (1.20.5+)
29+
30+
## Architecture
31+
32+
### Module Structure (71 modules total)
33+
34+
**Core Shared Modules:**
35+
- `DynmapCoreAPI/` - Stable public API for external plugins/mods (markers, mod support, rendering)
36+
- `DynmapCore/` - Internal shared implementation (NOT stable - subject to breaking changes)
37+
- `dynmap-api/` - Bukkit-specific public API
38+
39+
**Platform Implementations:**
40+
- `spigot/` - Bukkit/PaperMC implementation
41+
- `bukkit-helper-*` - Version-specific NMS code (25 versions: 1.13-1.21)
42+
- `fabric-*` - Fabric mod implementations (14 versions: 1.14.4-1.21.11)
43+
- `forge-*` - Forge mod implementations (14 versions: 1.14.4-1.21.11)
44+
45+
### Dependency Flow
46+
```
47+
External Plugins/Mods
48+
49+
DynmapCoreAPI (stable, published to repo.mikeprimm.com)
50+
51+
DynmapCore (internal, unstable)
52+
53+
Platform-specific modules (Spigot, Fabric, Forge)
54+
```
55+
56+
### Key Components in DynmapCore
57+
- `MapManager` - Tile/map rendering orchestration
58+
- `DynmapCore.java` - Main coordination hub (~3,100 lines)
59+
- `storage/` - Storage backends (FileTree, MySQL, PostgreSQL, SQLite, S3)
60+
- `hdmap/` - HD map rendering (block models, shaders, textures)
61+
- `web/` - Embedded Jetty server with servlets
62+
- `markers/` - Marker system implementation
63+
64+
## Critical Contribution Rules
65+
66+
**PRs must build and test on ALL platforms including oldgradle. Changes to DynmapCore/DynmapCoreAPI require testing on all platforms.**
67+
68+
- **Java 8 compatibility required** - Code must compile and run on Java 8
69+
- **Java only** - No Kotlin, Scala, or other JVM languages
70+
- **No dependency updates** - Library versions are tied to platform compatibility
71+
- **No platform-specific code** - Must work on Windows, Linux (x86/ARM), macOS, Docker
72+
- **Small PRs only** - One feature per PR, no style/formatting changes
73+
- **No mod-specific code** - Use Dynmap APIs instead; external mods should depend on DynmapCoreAPI
74+
- **Apache License v2** - All code must be compatible
75+
76+
## Testing
77+
78+
No automated tests exist. Verification is done by:
79+
1. Building all platforms successfully (`./gradlew setup build` AND `cd oldgradle && ./gradlew setup build`)
80+
2. Manual testing on target Minecraft server platforms
81+
82+
## Storage Backends
83+
84+
Dynmap supports: FileTree (default), MySQL/MariaDB, PostgreSQL, SQLite, MS SQL Server, AWS S3

DynmapCore/build.gradle

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@ dependencies {
2626
implementation 'io.github.linktosriram.s3lite:util:0.0.2-SNAPSHOT'
2727
implementation 'jakarta.xml.bind:jakarta.xml.bind-api:3.0.1'
2828
implementation 'com.sun.xml.bind:jaxb-impl:3.0.0'
29+
// Test dependencies (Java 8 compatible versions)
30+
testImplementation 'junit:junit:4.13.2'
31+
testImplementation 'org.mockito:mockito-core:4.11.0'
32+
testImplementation 'org.assertj:assertj-core:3.24.2'
33+
}
34+
35+
test {
36+
useJUnit()
37+
testLogging {
38+
events "passed", "skipped", "failed"
39+
exceptionFormat "full"
40+
}
2941
}
3042

3143
processResources {
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package org.dynmap.utils;
2+
3+
import org.junit.Test;
4+
import java.io.IOException;
5+
import static org.junit.Assert.*;
6+
7+
public class BufferInputStreamTest {
8+
9+
@Test
10+
public void testConstructorWithBuffer() {
11+
byte[] data = {1, 2, 3, 4, 5};
12+
BufferInputStream stream = new BufferInputStream(data);
13+
assertEquals(5, stream.length());
14+
assertSame(data, stream.buffer());
15+
}
16+
17+
@Test
18+
public void testConstructorWithBufferAndLength() {
19+
byte[] data = {1, 2, 3, 4, 5};
20+
BufferInputStream stream = new BufferInputStream(data, 3);
21+
assertEquals(3, stream.length());
22+
assertSame(data, stream.buffer());
23+
}
24+
25+
@Test
26+
public void testReadSingleByte() {
27+
byte[] data = {10, 20, 30};
28+
BufferInputStream stream = new BufferInputStream(data);
29+
assertEquals(10, stream.read());
30+
assertEquals(20, stream.read());
31+
assertEquals(30, stream.read());
32+
assertEquals(-1, stream.read()); // EOF
33+
}
34+
35+
@Test
36+
public void testReadSingleByteUnsigned() {
37+
// Test that bytes are returned as unsigned (0-255)
38+
byte[] data = {(byte) 255, (byte) 128};
39+
BufferInputStream stream = new BufferInputStream(data);
40+
assertEquals(255, stream.read());
41+
assertEquals(128, stream.read());
42+
}
43+
44+
@Test
45+
public void testReadArray() throws IOException {
46+
byte[] data = {1, 2, 3, 4, 5};
47+
BufferInputStream stream = new BufferInputStream(data);
48+
byte[] buffer = new byte[3];
49+
int bytesRead = stream.read(buffer, 0, 3);
50+
assertEquals(3, bytesRead);
51+
assertEquals(1, buffer[0]);
52+
assertEquals(2, buffer[1]);
53+
assertEquals(3, buffer[2]);
54+
}
55+
56+
@Test
57+
public void testReadArrayPartial() throws IOException {
58+
byte[] data = {1, 2, 3};
59+
BufferInputStream stream = new BufferInputStream(data);
60+
byte[] buffer = new byte[5];
61+
int bytesRead = stream.read(buffer, 0, 5);
62+
assertEquals(3, bytesRead);
63+
assertEquals(1, buffer[0]);
64+
assertEquals(2, buffer[1]);
65+
assertEquals(3, buffer[2]);
66+
}
67+
68+
@Test
69+
public void testReadArrayAtEOF() throws IOException {
70+
byte[] data = {1, 2};
71+
BufferInputStream stream = new BufferInputStream(data);
72+
stream.read();
73+
stream.read();
74+
byte[] buffer = new byte[5];
75+
int bytesRead = stream.read(buffer, 0, 5);
76+
assertEquals(-1, bytesRead);
77+
}
78+
79+
@Test(expected = IOException.class)
80+
public void testReadArrayNullBuffer() throws IOException {
81+
byte[] data = {1, 2, 3};
82+
BufferInputStream stream = new BufferInputStream(data);
83+
stream.read(null, 0, 3);
84+
}
85+
86+
@Test(expected = IOException.class)
87+
public void testReadArrayNegativeOffset() throws IOException {
88+
byte[] data = {1, 2, 3};
89+
BufferInputStream stream = new BufferInputStream(data);
90+
byte[] buffer = new byte[5];
91+
stream.read(buffer, -1, 3);
92+
}
93+
94+
@Test
95+
public void testAvailable() {
96+
byte[] data = {1, 2, 3, 4, 5};
97+
BufferInputStream stream = new BufferInputStream(data);
98+
assertEquals(5, stream.available());
99+
stream.read();
100+
assertEquals(4, stream.available());
101+
stream.read();
102+
stream.read();
103+
assertEquals(2, stream.available());
104+
}
105+
106+
@Test
107+
public void testSkip() {
108+
byte[] data = {1, 2, 3, 4, 5};
109+
BufferInputStream stream = new BufferInputStream(data);
110+
long skipped = stream.skip(2);
111+
assertEquals(2, skipped);
112+
assertEquals(3, stream.read());
113+
}
114+
115+
@Test
116+
public void testSkipPastEnd() {
117+
byte[] data = {1, 2, 3};
118+
BufferInputStream stream = new BufferInputStream(data);
119+
long skipped = stream.skip(10);
120+
assertEquals(3, skipped);
121+
assertEquals(-1, stream.read());
122+
}
123+
124+
@Test
125+
public void testSkipNegative() {
126+
byte[] data = {1, 2, 3};
127+
BufferInputStream stream = new BufferInputStream(data);
128+
stream.read();
129+
long skipped = stream.skip(-5);
130+
assertEquals(0, skipped);
131+
}
132+
133+
@Test
134+
public void testMarkSupported() {
135+
byte[] data = {1, 2, 3};
136+
BufferInputStream stream = new BufferInputStream(data);
137+
assertTrue(stream.markSupported());
138+
}
139+
140+
@Test
141+
public void testMarkAndReset() {
142+
byte[] data = {1, 2, 3, 4, 5};
143+
BufferInputStream stream = new BufferInputStream(data);
144+
stream.read();
145+
stream.read();
146+
stream.mark(0); // Mark at position 2
147+
assertEquals(3, stream.read());
148+
assertEquals(4, stream.read());
149+
stream.reset();
150+
assertEquals(3, stream.read()); // Back to position 2
151+
}
152+
153+
@Test
154+
public void testResetWithoutMark() {
155+
byte[] data = {1, 2, 3};
156+
BufferInputStream stream = new BufferInputStream(data);
157+
stream.read();
158+
stream.read();
159+
stream.reset(); // Should reset to 0 (default mark)
160+
assertEquals(1, stream.read());
161+
}
162+
163+
@Test
164+
public void testClose() {
165+
byte[] data = {1, 2, 3};
166+
BufferInputStream stream = new BufferInputStream(data);
167+
stream.close(); // Should not throw
168+
// Stream should still be usable after close (no-op)
169+
assertEquals(1, stream.read());
170+
}
171+
172+
@Test
173+
public void testEmptyBuffer() {
174+
byte[] data = {};
175+
BufferInputStream stream = new BufferInputStream(data);
176+
assertEquals(0, stream.available());
177+
assertEquals(-1, stream.read());
178+
}
179+
180+
@Test
181+
public void testLengthVsBufferLength() {
182+
byte[] data = {1, 2, 3, 4, 5};
183+
BufferInputStream stream = new BufferInputStream(data, 2);
184+
assertEquals(2, stream.available());
185+
assertEquals(1, stream.read());
186+
assertEquals(2, stream.read());
187+
assertEquals(-1, stream.read()); // EOF at specified length
188+
}
189+
}

0 commit comments

Comments
 (0)