Claude Code tire-kicking: generate some unit tests

This commit is contained in:
Mike Primm 2026-02-06 08:37:31 -05:00
parent c375665a6d
commit 5a30ab0d3f
7 changed files with 1025 additions and 0 deletions

84
CLAUDE.md Normal file
View file

@ -0,0 +1,84 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
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).
## Build Commands
```bash
# Build all platforms (requires JDK 21 as default)
./gradlew setup build
# Build outputs go to /target directory
# Build specific module (for faster iteration, but NOT for PR submissions)
./gradlew :fabric-1.18:build
# Forge 1.12.2 (requires JDK 8 - set JAVA_HOME accordingly)
cd oldgradle
./gradlew setup build
```
**JDK Requirements:**
- Default: JDK 21
- Forge 1.12.2 (oldgradle): JDK 8 strictly required
- Runtime targets: JDK 8 (1.16-), JDK 16 (1.17.x), JDK 17 (1.18-1.20.4), JDK 21 (1.20.5+)
## Architecture
### Module Structure (71 modules total)
**Core Shared Modules:**
- `DynmapCoreAPI/` - Stable public API for external plugins/mods (markers, mod support, rendering)
- `DynmapCore/` - Internal shared implementation (NOT stable - subject to breaking changes)
- `dynmap-api/` - Bukkit-specific public API
**Platform Implementations:**
- `spigot/` - Bukkit/PaperMC implementation
- `bukkit-helper-*` - Version-specific NMS code (25 versions: 1.13-1.21)
- `fabric-*` - Fabric mod implementations (14 versions: 1.14.4-1.21.11)
- `forge-*` - Forge mod implementations (14 versions: 1.14.4-1.21.11)
### Dependency Flow
```
External Plugins/Mods
DynmapCoreAPI (stable, published to repo.mikeprimm.com)
DynmapCore (internal, unstable)
Platform-specific modules (Spigot, Fabric, Forge)
```
### Key Components in DynmapCore
- `MapManager` - Tile/map rendering orchestration
- `DynmapCore.java` - Main coordination hub (~3,100 lines)
- `storage/` - Storage backends (FileTree, MySQL, PostgreSQL, SQLite, S3)
- `hdmap/` - HD map rendering (block models, shaders, textures)
- `web/` - Embedded Jetty server with servlets
- `markers/` - Marker system implementation
## Critical Contribution Rules
**PRs must build and test on ALL platforms including oldgradle. Changes to DynmapCore/DynmapCoreAPI require testing on all platforms.**
- **Java 8 compatibility required** - Code must compile and run on Java 8
- **Java only** - No Kotlin, Scala, or other JVM languages
- **No dependency updates** - Library versions are tied to platform compatibility
- **No platform-specific code** - Must work on Windows, Linux (x86/ARM), macOS, Docker
- **Small PRs only** - One feature per PR, no style/formatting changes
- **No mod-specific code** - Use Dynmap APIs instead; external mods should depend on DynmapCoreAPI
- **Apache License v2** - All code must be compatible
## Testing
No automated tests exist. Verification is done by:
1. Building all platforms successfully (`./gradlew setup build` AND `cd oldgradle && ./gradlew setup build`)
2. Manual testing on target Minecraft server platforms
## Storage Backends
Dynmap supports: FileTree (default), MySQL/MariaDB, PostgreSQL, SQLite, MS SQL Server, AWS S3

View file

@ -26,6 +26,18 @@ dependencies {
implementation 'io.github.linktosriram.s3lite:util:0.0.2-SNAPSHOT' implementation 'io.github.linktosriram.s3lite:util:0.0.2-SNAPSHOT'
implementation 'jakarta.xml.bind:jakarta.xml.bind-api:3.0.1' implementation 'jakarta.xml.bind:jakarta.xml.bind-api:3.0.1'
implementation 'com.sun.xml.bind:jaxb-impl:3.0.0' implementation 'com.sun.xml.bind:jaxb-impl:3.0.0'
// Test dependencies (Java 8 compatible versions)
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:4.11.0'
testImplementation 'org.assertj:assertj-core:3.24.2'
}
test {
useJUnit()
testLogging {
events "passed", "skipped", "failed"
exceptionFormat "full"
}
} }
processResources { processResources {

View file

@ -0,0 +1,189 @@
package org.dynmap.utils;
import org.junit.Test;
import java.io.IOException;
import static org.junit.Assert.*;
public class BufferInputStreamTest {
@Test
public void testConstructorWithBuffer() {
byte[] data = {1, 2, 3, 4, 5};
BufferInputStream stream = new BufferInputStream(data);
assertEquals(5, stream.length());
assertSame(data, stream.buffer());
}
@Test
public void testConstructorWithBufferAndLength() {
byte[] data = {1, 2, 3, 4, 5};
BufferInputStream stream = new BufferInputStream(data, 3);
assertEquals(3, stream.length());
assertSame(data, stream.buffer());
}
@Test
public void testReadSingleByte() {
byte[] data = {10, 20, 30};
BufferInputStream stream = new BufferInputStream(data);
assertEquals(10, stream.read());
assertEquals(20, stream.read());
assertEquals(30, stream.read());
assertEquals(-1, stream.read()); // EOF
}
@Test
public void testReadSingleByteUnsigned() {
// Test that bytes are returned as unsigned (0-255)
byte[] data = {(byte) 255, (byte) 128};
BufferInputStream stream = new BufferInputStream(data);
assertEquals(255, stream.read());
assertEquals(128, stream.read());
}
@Test
public void testReadArray() throws IOException {
byte[] data = {1, 2, 3, 4, 5};
BufferInputStream stream = new BufferInputStream(data);
byte[] buffer = new byte[3];
int bytesRead = stream.read(buffer, 0, 3);
assertEquals(3, bytesRead);
assertEquals(1, buffer[0]);
assertEquals(2, buffer[1]);
assertEquals(3, buffer[2]);
}
@Test
public void testReadArrayPartial() throws IOException {
byte[] data = {1, 2, 3};
BufferInputStream stream = new BufferInputStream(data);
byte[] buffer = new byte[5];
int bytesRead = stream.read(buffer, 0, 5);
assertEquals(3, bytesRead);
assertEquals(1, buffer[0]);
assertEquals(2, buffer[1]);
assertEquals(3, buffer[2]);
}
@Test
public void testReadArrayAtEOF() throws IOException {
byte[] data = {1, 2};
BufferInputStream stream = new BufferInputStream(data);
stream.read();
stream.read();
byte[] buffer = new byte[5];
int bytesRead = stream.read(buffer, 0, 5);
assertEquals(-1, bytesRead);
}
@Test(expected = IOException.class)
public void testReadArrayNullBuffer() throws IOException {
byte[] data = {1, 2, 3};
BufferInputStream stream = new BufferInputStream(data);
stream.read(null, 0, 3);
}
@Test(expected = IOException.class)
public void testReadArrayNegativeOffset() throws IOException {
byte[] data = {1, 2, 3};
BufferInputStream stream = new BufferInputStream(data);
byte[] buffer = new byte[5];
stream.read(buffer, -1, 3);
}
@Test
public void testAvailable() {
byte[] data = {1, 2, 3, 4, 5};
BufferInputStream stream = new BufferInputStream(data);
assertEquals(5, stream.available());
stream.read();
assertEquals(4, stream.available());
stream.read();
stream.read();
assertEquals(2, stream.available());
}
@Test
public void testSkip() {
byte[] data = {1, 2, 3, 4, 5};
BufferInputStream stream = new BufferInputStream(data);
long skipped = stream.skip(2);
assertEquals(2, skipped);
assertEquals(3, stream.read());
}
@Test
public void testSkipPastEnd() {
byte[] data = {1, 2, 3};
BufferInputStream stream = new BufferInputStream(data);
long skipped = stream.skip(10);
assertEquals(3, skipped);
assertEquals(-1, stream.read());
}
@Test
public void testSkipNegative() {
byte[] data = {1, 2, 3};
BufferInputStream stream = new BufferInputStream(data);
stream.read();
long skipped = stream.skip(-5);
assertEquals(0, skipped);
}
@Test
public void testMarkSupported() {
byte[] data = {1, 2, 3};
BufferInputStream stream = new BufferInputStream(data);
assertTrue(stream.markSupported());
}
@Test
public void testMarkAndReset() {
byte[] data = {1, 2, 3, 4, 5};
BufferInputStream stream = new BufferInputStream(data);
stream.read();
stream.read();
stream.mark(0); // Mark at position 2
assertEquals(3, stream.read());
assertEquals(4, stream.read());
stream.reset();
assertEquals(3, stream.read()); // Back to position 2
}
@Test
public void testResetWithoutMark() {
byte[] data = {1, 2, 3};
BufferInputStream stream = new BufferInputStream(data);
stream.read();
stream.read();
stream.reset(); // Should reset to 0 (default mark)
assertEquals(1, stream.read());
}
@Test
public void testClose() {
byte[] data = {1, 2, 3};
BufferInputStream stream = new BufferInputStream(data);
stream.close(); // Should not throw
// Stream should still be usable after close (no-op)
assertEquals(1, stream.read());
}
@Test
public void testEmptyBuffer() {
byte[] data = {};
BufferInputStream stream = new BufferInputStream(data);
assertEquals(0, stream.available());
assertEquals(-1, stream.read());
}
@Test
public void testLengthVsBufferLength() {
byte[] data = {1, 2, 3, 4, 5};
BufferInputStream stream = new BufferInputStream(data, 2);
assertEquals(2, stream.available());
assertEquals(1, stream.read());
assertEquals(2, stream.read());
assertEquals(-1, stream.read()); // EOF at specified length
}
}

View file

@ -0,0 +1,249 @@
package org.dynmap.utils;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.*;
public class DynIntHashMapTest {
@Test
public void testDefaultConstructor() {
DynIntHashMap map = new DynIntHashMap();
assertTrue(map.isEmpty());
assertEquals(0, map.size());
}
@Test
public void testConstructorWithCapacity() {
DynIntHashMap map = new DynIntHashMap(100);
assertTrue(map.isEmpty());
assertEquals(0, map.size());
}
@Test
public void testConstructorWithCapacityAndLoadFactor() {
DynIntHashMap map = new DynIntHashMap(100, 0.5f);
assertTrue(map.isEmpty());
assertEquals(0, map.size());
}
@Test(expected = IllegalArgumentException.class)
public void testConstructorNegativeCapacity() {
new DynIntHashMap(-1);
}
@Test(expected = IllegalArgumentException.class)
public void testConstructorZeroLoadFactor() {
new DynIntHashMap(10, 0.0f);
}
@Test(expected = IllegalArgumentException.class)
public void testConstructorNegativeLoadFactor() {
new DynIntHashMap(10, -0.5f);
}
@Test
public void testCopyConstructor() {
DynIntHashMap original = new DynIntHashMap();
original.put(1, "one");
original.put(2, "two");
DynIntHashMap copy = new DynIntHashMap(original);
assertEquals(2, copy.size());
assertEquals("one", copy.get(1));
assertEquals("two", copy.get(2));
// Verify it's a deep copy
original.put(1, "modified");
assertEquals("one", copy.get(1));
}
@Test
public void testPutAndGet() {
DynIntHashMap map = new DynIntHashMap();
assertNull(map.put(1, "one"));
assertEquals("one", map.get(1));
assertEquals(1, map.size());
}
@Test
public void testPutReplace() {
DynIntHashMap map = new DynIntHashMap();
map.put(1, "one");
Object oldValue = map.put(1, "ONE");
assertEquals("one", oldValue);
assertEquals("ONE", map.get(1));
assertEquals(1, map.size());
}
@Test
public void testPutNullValue() {
DynIntHashMap map = new DynIntHashMap();
map.put(1, null);
assertNull(map.get(1));
assertTrue(map.containsKey(1));
}
@Test
public void testGetNonExistent() {
DynIntHashMap map = new DynIntHashMap();
assertNull(map.get(999));
}
@Test
public void testContainsKey() {
DynIntHashMap map = new DynIntHashMap();
map.put(1, "one");
assertTrue(map.containsKey(1));
assertFalse(map.containsKey(2));
}
@Test
public void testContainsValue() {
DynIntHashMap map = new DynIntHashMap();
map.put(1, "one");
map.put(2, "two");
assertTrue(map.containsValue("one"));
assertTrue(map.containsValue("two"));
assertFalse(map.containsValue("three"));
}
@Test
public void testContainsValueNull() {
DynIntHashMap map = new DynIntHashMap();
map.put(1, null);
assertTrue(map.containsValue(null));
}
@Test
public void testRemove() {
DynIntHashMap map = new DynIntHashMap();
map.put(1, "one");
map.put(2, "two");
Object removed = map.remove(1);
assertEquals("one", removed);
assertEquals(1, map.size());
assertFalse(map.containsKey(1));
assertTrue(map.containsKey(2));
}
@Test
public void testRemoveNonExistent() {
DynIntHashMap map = new DynIntHashMap();
map.put(1, "one");
Object removed = map.remove(999);
assertNull(removed);
assertEquals(1, map.size());
}
@Test
public void testClear() {
DynIntHashMap map = new DynIntHashMap();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.clear();
assertTrue(map.isEmpty());
assertEquals(0, map.size());
assertNull(map.get(1));
}
@Test
public void testIsEmpty() {
DynIntHashMap map = new DynIntHashMap();
assertTrue(map.isEmpty());
map.put(1, "one");
assertFalse(map.isEmpty());
map.remove(1);
assertTrue(map.isEmpty());
}
@Test
public void testKeysWithValue() {
DynIntHashMap map = new DynIntHashMap();
map.put(1, "value");
map.put(2, "value");
map.put(3, "other");
List<Integer> keys = map.keysWithValue("value");
assertEquals(2, keys.size());
assertTrue(keys.contains(1));
assertTrue(keys.contains(2));
}
@Test
public void testKeysWithValueNull() {
DynIntHashMap map = new DynIntHashMap();
map.put(1, null);
map.put(2, null);
map.put(3, "value");
List<Integer> keys = map.keysWithValue(null);
assertEquals(2, keys.size());
assertTrue(keys.contains(1));
assertTrue(keys.contains(2));
}
@Test
public void testKeysWithValueNoMatch() {
DynIntHashMap map = new DynIntHashMap();
map.put(1, "one");
map.put(2, "two");
List<Integer> keys = map.keysWithValue("nonexistent");
assertTrue(keys.isEmpty());
}
@Test
public void testRehashing() {
// Use small initial capacity to trigger rehash
DynIntHashMap map = new DynIntHashMap(2, 0.75f);
// Add enough entries to trigger rehash
for (int i = 0; i < 100; i++) {
map.put(i, "value" + i);
}
assertEquals(100, map.size());
// Verify all entries are still accessible
for (int i = 0; i < 100; i++) {
assertEquals("value" + i, map.get(i));
}
}
@Test
public void testNegativeKey() {
DynIntHashMap map = new DynIntHashMap();
map.put(-1, "negative");
assertEquals("negative", map.get(-1));
assertTrue(map.containsKey(-1));
}
@Test
public void testZeroKey() {
DynIntHashMap map = new DynIntHashMap();
map.put(0, "zero");
assertEquals("zero", map.get(0));
assertTrue(map.containsKey(0));
}
@Test
public void testMaxIntKey() {
DynIntHashMap map = new DynIntHashMap();
map.put(Integer.MAX_VALUE, "max");
assertEquals("max", map.get(Integer.MAX_VALUE));
}
@Test
public void testMinIntKey() {
DynIntHashMap map = new DynIntHashMap();
map.put(Integer.MIN_VALUE, "min");
assertEquals("min", map.get(Integer.MIN_VALUE));
}
}

View file

@ -0,0 +1,122 @@
package org.dynmap.utils;
import org.junit.Test;
import static org.junit.Assert.*;
public class IpAddressMatcherTest {
// IPv4 exact match tests
@Test
public void testIPv4ExactMatch() {
IpAddressMatcher matcher = new IpAddressMatcher("192.168.1.100");
assertTrue(matcher.matches("192.168.1.100"));
}
@Test
public void testIPv4ExactMatchNoMatch() {
IpAddressMatcher matcher = new IpAddressMatcher("192.168.1.100");
assertFalse(matcher.matches("192.168.1.101"));
}
@Test
public void testIPv4Localhost() {
IpAddressMatcher matcher = new IpAddressMatcher("127.0.0.1");
assertTrue(matcher.matches("127.0.0.1"));
assertFalse(matcher.matches("127.0.0.2"));
}
// IPv4 CIDR tests
@Test
public void testIPv4Cidr24() {
IpAddressMatcher matcher = new IpAddressMatcher("192.168.1.0/24");
assertTrue(matcher.matches("192.168.1.0"));
assertTrue(matcher.matches("192.168.1.1"));
assertTrue(matcher.matches("192.168.1.255"));
assertFalse(matcher.matches("192.168.2.1"));
}
@Test
public void testIPv4Cidr16() {
IpAddressMatcher matcher = new IpAddressMatcher("192.168.0.0/16");
assertTrue(matcher.matches("192.168.0.1"));
assertTrue(matcher.matches("192.168.255.255"));
assertFalse(matcher.matches("192.169.0.1"));
}
@Test
public void testIPv4Cidr8() {
IpAddressMatcher matcher = new IpAddressMatcher("10.0.0.0/8");
assertTrue(matcher.matches("10.0.0.1"));
assertTrue(matcher.matches("10.255.255.255"));
assertFalse(matcher.matches("11.0.0.1"));
}
@Test
public void testIPv4Cidr32() {
IpAddressMatcher matcher = new IpAddressMatcher("192.168.1.100/32");
assertTrue(matcher.matches("192.168.1.100"));
assertFalse(matcher.matches("192.168.1.101"));
}
@Test
public void testIPv4CidrZero() {
IpAddressMatcher matcher = new IpAddressMatcher("0.0.0.0/0");
assertTrue(matcher.matches("192.168.1.1"));
assertTrue(matcher.matches("10.0.0.1"));
assertTrue(matcher.matches("255.255.255.255"));
}
// IPv6 tests
@Test
public void testIPv6ExactMatch() {
IpAddressMatcher matcher = new IpAddressMatcher("::1");
assertTrue(matcher.matches("::1"));
assertTrue(matcher.matches("0:0:0:0:0:0:0:1"));
}
@Test
public void testIPv6Cidr() {
IpAddressMatcher matcher = new IpAddressMatcher("2001:db8::/32");
assertTrue(matcher.matches("2001:db8::1"));
assertTrue(matcher.matches("2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"));
assertFalse(matcher.matches("2001:db9::1"));
}
// Cross-family rejection tests
@Test
public void testIPv4DoesNotMatchIPv6() {
IpAddressMatcher matcher = new IpAddressMatcher("192.168.1.0/24");
assertFalse(matcher.matches("::1"));
}
@Test
public void testIPv6DoesNotMatchIPv4() {
IpAddressMatcher matcher = new IpAddressMatcher("::1");
assertFalse(matcher.matches("127.0.0.1"));
}
// Edge cases
@Test(expected = IllegalArgumentException.class)
public void testInvalidIPAddress() {
new IpAddressMatcher("invalid.ip.address");
}
@Test
public void testPrivateNetworkRanges() {
// Class A private
IpAddressMatcher classA = new IpAddressMatcher("10.0.0.0/8");
assertTrue(classA.matches("10.0.0.1"));
assertTrue(classA.matches("10.255.255.254"));
// Class B private
IpAddressMatcher classB = new IpAddressMatcher("172.16.0.0/12");
assertTrue(classB.matches("172.16.0.1"));
assertTrue(classB.matches("172.31.255.254"));
assertFalse(classB.matches("172.32.0.1"));
// Class C private
IpAddressMatcher classC = new IpAddressMatcher("192.168.0.0/16");
assertTrue(classC.matches("192.168.0.1"));
assertTrue(classC.matches("192.168.255.254"));
}
}

View file

@ -0,0 +1,196 @@
package org.dynmap.utils;
import org.junit.Test;
import static org.junit.Assert.*;
public class Matrix3DTest {
private static final double DELTA = 1e-10;
@Test
public void testIdentityConstructor() {
Matrix3D m = new Matrix3D();
double[] v = {1.0, 2.0, 3.0};
m.transform(v);
// Identity matrix should not change the vector
assertEquals(1.0, v[0], DELTA);
assertEquals(2.0, v[1], DELTA);
assertEquals(3.0, v[2], DELTA);
}
@Test
public void testParameterizedConstructor() {
// Create a matrix that doubles each component
Matrix3D m = new Matrix3D(2, 0, 0, 0, 2, 0, 0, 0, 2);
double[] v = {1.0, 2.0, 3.0};
m.transform(v);
assertEquals(2.0, v[0], DELTA);
assertEquals(4.0, v[1], DELTA);
assertEquals(6.0, v[2], DELTA);
}
@Test
public void testTransformArray() {
Matrix3D m = new Matrix3D(1, 2, 3, 4, 5, 6, 7, 8, 9);
double[] v = {1.0, 1.0, 1.0};
m.transform(v);
// Row 1: 1*1 + 2*1 + 3*1 = 6
// Row 2: 4*1 + 5*1 + 6*1 = 15
// Row 3: 7*1 + 8*1 + 9*1 = 24
assertEquals(6.0, v[0], DELTA);
assertEquals(15.0, v[1], DELTA);
assertEquals(24.0, v[2], DELTA);
}
@Test
public void testTransformVector3D() {
Matrix3D m = new Matrix3D(1, 2, 3, 4, 5, 6, 7, 8, 9);
Vector3D v = new Vector3D(1.0, 1.0, 1.0);
m.transform(v);
assertEquals(6.0, v.x, DELTA);
assertEquals(15.0, v.y, DELTA);
assertEquals(24.0, v.z, DELTA);
}
@Test
public void testTransformVector3DWithOutput() {
Matrix3D m = new Matrix3D(1, 2, 3, 4, 5, 6, 7, 8, 9);
Vector3D v = new Vector3D(1.0, 1.0, 1.0);
Vector3D out = new Vector3D();
m.transform(v, out);
// Input should be unchanged
assertEquals(1.0, v.x, DELTA);
assertEquals(1.0, v.y, DELTA);
assertEquals(1.0, v.z, DELTA);
// Output should have transformed values
assertEquals(6.0, out.x, DELTA);
assertEquals(15.0, out.y, DELTA);
assertEquals(24.0, out.z, DELTA);
}
@Test
public void testScale() {
Matrix3D m = new Matrix3D();
m.scale(2.0, 3.0, 4.0);
double[] v = {1.0, 1.0, 1.0};
m.transform(v);
assertEquals(2.0, v[0], DELTA);
assertEquals(3.0, v[1], DELTA);
assertEquals(4.0, v[2], DELTA);
}
@Test
public void testRotateXY90Degrees() {
Matrix3D m = new Matrix3D();
m.rotateXY(90.0);
double[] v = {1.0, 0.0, 0.0};
m.transform(v);
// X axis rotated 90 degrees clockwise around Z should point to Y
assertEquals(0.0, v[0], DELTA);
assertEquals(-1.0, v[1], DELTA);
assertEquals(0.0, v[2], DELTA);
}
@Test
public void testRotateXZ90Degrees() {
Matrix3D m = new Matrix3D();
m.rotateXZ(90.0);
double[] v = {1.0, 0.0, 0.0};
m.transform(v);
// X axis rotated 90 degrees clockwise around Y should point to +Z
assertEquals(0.0, v[0], DELTA);
assertEquals(0.0, v[1], DELTA);
assertEquals(1.0, v[2], DELTA);
}
@Test
public void testRotateYZ90Degrees() {
Matrix3D m = new Matrix3D();
m.rotateYZ(90.0);
double[] v = {0.0, 1.0, 0.0};
m.transform(v);
// Y axis rotated 90 degrees clockwise around X should point to Z
assertEquals(0.0, v[0], DELTA);
assertEquals(0.0, v[1], DELTA);
assertEquals(-1.0, v[2], DELTA);
}
@Test
public void testRotate360DegreesReturnsToOriginal() {
Matrix3D m = new Matrix3D();
m.rotateXY(360.0);
double[] v = {1.0, 2.0, 3.0};
m.transform(v);
assertEquals(1.0, v[0], DELTA);
assertEquals(2.0, v[1], DELTA);
assertEquals(3.0, v[2], DELTA);
}
@Test
public void testMultiply() {
// Create two scaling matrices
Matrix3D m1 = new Matrix3D(2, 0, 0, 0, 2, 0, 0, 0, 2);
Matrix3D m2 = new Matrix3D(3, 0, 0, 0, 3, 0, 0, 0, 3);
m1.multiply(m2);
double[] v = {1.0, 1.0, 1.0};
m1.transform(v);
// Should scale by 6 (2*3)
assertEquals(6.0, v[0], DELTA);
assertEquals(6.0, v[1], DELTA);
assertEquals(6.0, v[2], DELTA);
}
@Test
public void testShearZ() {
Matrix3D m = new Matrix3D();
m.shearZ(1.0, 0.0);
// With shearZ matrix [1,0,0; 0,1,0; x_fact,y_fact,1] multiplied as mat*this
// The result matrix has x_fact in position m31
// transform: v1 = m11*x + m12*y + m13*z = x
// v2 = m21*x + m22*y + m23*z = y
// v3 = m31*x + m32*y + m33*z = x_fact*x + y_fact*y + z
double[] v = {1.0, 0.0, 0.0};
m.transform(v);
// X contributes to Z via shear
assertEquals(1.0, v[0], DELTA);
assertEquals(0.0, v[1], DELTA);
assertEquals(1.0, v[2], DELTA);
}
@Test
public void testChainedTransformations() {
Matrix3D m = new Matrix3D();
m.scale(2.0, 2.0, 2.0);
m.rotateXY(90.0);
double[] v = {1.0, 0.0, 0.0};
m.transform(v);
// First scale by 2, then rotate - result should be scaled and rotated
assertEquals(0.0, v[0], DELTA);
assertEquals(-2.0, v[1], DELTA);
assertEquals(0.0, v[2], DELTA);
}
@Test
public void testToString() {
Matrix3D m = new Matrix3D();
String str = m.toString();
assertNotNull(str);
assertTrue(str.contains("1.0"));
}
@Test
public void testToJSON() {
Matrix3D m = new Matrix3D(1, 2, 3, 4, 5, 6, 7, 8, 9);
org.json.simple.JSONArray json = m.toJSON();
assertEquals(9, json.size());
assertEquals(1.0, json.get(0));
assertEquals(2.0, json.get(1));
assertEquals(3.0, json.get(2));
assertEquals(4.0, json.get(3));
assertEquals(5.0, json.get(4));
assertEquals(6.0, json.get(5));
assertEquals(7.0, json.get(6));
assertEquals(8.0, json.get(7));
assertEquals(9.0, json.get(8));
}
}

View file

@ -0,0 +1,173 @@
package org.dynmap.utils;
import org.junit.Test;
import static org.junit.Assert.*;
public class Vector3DTest {
private static final double DELTA = 1e-10;
@Test
public void testDefaultConstructor() {
Vector3D v = new Vector3D();
assertEquals(0.0, v.x, DELTA);
assertEquals(0.0, v.y, DELTA);
assertEquals(0.0, v.z, DELTA);
}
@Test
public void testParameterizedConstructor() {
Vector3D v = new Vector3D(1.0, 2.0, 3.0);
assertEquals(1.0, v.x, DELTA);
assertEquals(2.0, v.y, DELTA);
assertEquals(3.0, v.z, DELTA);
}
@Test
public void testCopyConstructor() {
Vector3D original = new Vector3D(5.0, 6.0, 7.0);
Vector3D copy = new Vector3D(original);
assertEquals(original.x, copy.x, DELTA);
assertEquals(original.y, copy.y, DELTA);
assertEquals(original.z, copy.z, DELTA);
// Ensure it's a copy, not a reference
original.x = 100.0;
assertEquals(5.0, copy.x, DELTA);
}
@Test
public void testSet() {
Vector3D v1 = new Vector3D(1.0, 2.0, 3.0);
Vector3D v2 = new Vector3D();
Vector3D result = v2.set(v1);
assertEquals(1.0, v2.x, DELTA);
assertEquals(2.0, v2.y, DELTA);
assertEquals(3.0, v2.z, DELTA);
assertSame(v2, result);
}
@Test
public void testAdd() {
Vector3D v1 = new Vector3D(1.0, 2.0, 3.0);
Vector3D v2 = new Vector3D(4.0, 5.0, 6.0);
Vector3D result = v1.add(v2);
assertEquals(5.0, v1.x, DELTA);
assertEquals(7.0, v1.y, DELTA);
assertEquals(9.0, v1.z, DELTA);
assertSame(v1, result);
}
@Test
public void testSubtract() {
Vector3D v1 = new Vector3D(5.0, 7.0, 9.0);
Vector3D v2 = new Vector3D(1.0, 2.0, 3.0);
Vector3D result = v1.subtract(v2);
assertEquals(4.0, v1.x, DELTA);
assertEquals(5.0, v1.y, DELTA);
assertEquals(6.0, v1.z, DELTA);
assertSame(v1, result);
}
@Test
public void testScale() {
Vector3D v = new Vector3D(2.0, 3.0, 4.0);
Vector3D result = v.scale(2.0);
assertEquals(4.0, v.x, DELTA);
assertEquals(6.0, v.y, DELTA);
assertEquals(8.0, v.z, DELTA);
assertSame(v, result);
}
@Test
public void testInnerProduct() {
Vector3D v1 = new Vector3D(1.0, 2.0, 3.0);
Vector3D v2 = new Vector3D(4.0, 5.0, 6.0);
double result = v1.innerProduct(v2);
// 1*4 + 2*5 + 3*6 = 4 + 10 + 18 = 32
assertEquals(32.0, result, DELTA);
}
@Test
public void testCrossProduct() {
Vector3D v1 = new Vector3D(1.0, 0.0, 0.0);
Vector3D v2 = new Vector3D(0.0, 1.0, 0.0);
Vector3D result = v1.crossProduct(v2);
// i x j = k
assertEquals(0.0, v1.x, DELTA);
assertEquals(0.0, v1.y, DELTA);
assertEquals(1.0, v1.z, DELTA);
assertSame(v1, result);
}
@Test
public void testCrossProductGeneral() {
Vector3D v1 = new Vector3D(2.0, 3.0, 4.0);
Vector3D v2 = new Vector3D(5.0, 6.0, 7.0);
v1.crossProduct(v2);
// (3*7 - 4*6, 4*5 - 2*7, 2*6 - 3*5) = (21-24, 20-14, 12-15) = (-3, 6, -3)
assertEquals(-3.0, v1.x, DELTA);
assertEquals(6.0, v1.y, DELTA);
assertEquals(-3.0, v1.z, DELTA);
}
@Test
public void testLength() {
Vector3D v = new Vector3D(3.0, 4.0, 0.0);
assertEquals(5.0, v.length(), DELTA);
}
@Test
public void testLengthUnit() {
Vector3D v = new Vector3D(1.0, 0.0, 0.0);
assertEquals(1.0, v.length(), DELTA);
}
@Test
public void testLengthZero() {
Vector3D v = new Vector3D();
assertEquals(0.0, v.length(), DELTA);
}
@Test
public void testEquals() {
Vector3D v1 = new Vector3D(1.0, 2.0, 3.0);
Vector3D v2 = new Vector3D(1.0, 2.0, 3.0);
assertTrue(v1.equals(v2));
assertTrue(v2.equals(v1));
}
@Test
public void testEqualsNotEqual() {
Vector3D v1 = new Vector3D(1.0, 2.0, 3.0);
Vector3D v2 = new Vector3D(1.0, 2.0, 4.0);
assertFalse(v1.equals(v2));
}
@Test
public void testEqualsNull() {
Vector3D v = new Vector3D(1.0, 2.0, 3.0);
assertFalse(v.equals(null));
}
@Test
public void testEqualsWrongType() {
Vector3D v = new Vector3D(1.0, 2.0, 3.0);
assertFalse(v.equals("not a vector"));
}
@Test
public void testHashCodeConsistency() {
Vector3D v1 = new Vector3D(1.0, 2.0, 3.0);
Vector3D v2 = new Vector3D(1.0, 2.0, 3.0);
assertEquals(v1.hashCode(), v2.hashCode());
}
@Test
public void testToString() {
Vector3D v = new Vector3D(1.0, 2.0, 3.0);
String str = v.toString();
assertTrue(str.contains("1.0"));
assertTrue(str.contains("2.0"));
assertTrue(str.contains("3.0"));
}
}