Skip to content

iJaco25/hytestkit

Repository files navigation

HyTestKit

A fluent integration testing framework for Hytale server mods.

Installation

Option 1: Local Maven

./gradlew publishToMavenLocal

Then in your project's build.gradle.kts:

repositories {
    mavenLocal()
}

dependencies {
    implementation("net.mono:hytestkit:0.1.0")
}

Option 2: Include as source

Copy the src/main/java/net/mono/hytestkit package into your project.

Core Components

TestRunner

Generic test runner that executes scenarios and reports results.

TestRunner<MyContext> runner = new TestRunner<>(logger -> new MyContext(logger));
runner.register(new MyTestScenario());
runner.runAll(System.out::println);

Test Builder

Fluent API for building test scenarios:

Test.<MyContext>named("my-test")
    .describedAs("Tests something important")
    .setup(ctx -> { /* initialization */ })
    .action(ctx -> { /* do something */ })
    .action(ctx -> { /* do something else */ })
    .verify(ctx -> someCondition)
    .onFailure("Expected X but got Y")
    .teardown(ctx -> { /* cleanup */ })
    .build();

TestScenario

Interface for implementing custom test scenarios:

public class MyScenario implements TestScenario<MyContext> {
    @Override
    public String getName() { return "my-scenario"; }

    @Override
    public String getDescription() { return "Tests something"; }

    @Override
    public TestOutcome execute(MyContext context) {
        // test logic
        return TestOutcome.passed();
    }
}

Actions

Reusable test actions:

Actions.delay(1000)                    // Sleep 1 second
Actions.repeat(5, action)              // Execute action 5 times
Actions.repeat(5, 100, action)         // Execute 5 times with 100ms delay
Actions.retry(3, action)               // Retry up to 3 times on failure
Actions.retry(3, 500, action)          // Retry with 500ms delay between
Actions.sequence(action1, action2)     // Execute actions in order
Actions.when(predicate, action)        // Conditional execution
Actions.ifElse(pred, ifTrue, ifFalse)  // Branch execution
Actions.log("message")                 // Log message (requires WithLogger)
Actions.store("key", value)            // Store value (requires WithStorage)
Actions.markStart()                    // Mark timing start (requires WithTiming)

Predicates

Reusable test predicates:

Predicates.alwaysTrue()
Predicates.alwaysFalse()
Predicates.equals(ctx -> ctx.getValue(), expected)
Predicates.notNull(ctx -> ctx.getValue())
Predicates.greaterThan(ctx -> ctx.getNumber(), 10.0)
Predicates.between(ctx -> ctx.getNumber(), 0.0, 100.0)
Predicates.stringContains(ctx -> ctx.getText(), "substring")
Predicates.eventually(predicate, 5000)           // Wait up to 5s for true
Predicates.eventually(predicate, 5000, 100)      // With 100ms poll interval
Predicates.stored("key", Integer.class, v -> v > 0)  // Test stored value

TestContext Interfaces

Implement these interfaces to enable specific Actions/Predicates:

public class MyContext implements
        TestContext.WithStorage,
        TestContext.WithLogger,
        TestContext.WithTiming {

    // WithStorage
    public <V> void store(String key, V value) { ... }
    public <V> V get(String key, Class<V> type) { ... }
    public boolean has(String key) { ... }

    // WithLogger
    public void log(String message) { ... }

    // WithTiming
    public void markStart() { ... }
    public long elapsedMs() { ... }
}

Hytale ECS Integration

The hytale subpackage provides ECS-specific utilities.

EcsTestContext

Base context with entity management:

EcsTestContext ctx = new EcsTestContext(world, System.out::println);

// Spawn entity
ctx.spawnEntity(
    EcsEntityBuilder.create()
        .withComponent(TransformComponent.getComponentType(), transform)
        .withComponent(Velocity.getComponentType(), new Velocity()),
    x, y, z
).join();

EcsEntityBuilder

Fluent entity creation:

EcsEntityBuilder.create()
    .withComponent(TransformComponent.getComponentType(), transform)
    .withComponent(Velocity.getComponentType(), new Velocity())
    .withLazyComponent(MyComponent.getComponentType(), MyComponent::new)
    .configure(holder -> { /* custom setup */ })
    .withAddReason(AddReason.SPAWN)
    .spawn(world, x, y, z);

EcsActions

Entity manipulation actions:

EcsActions.teleport(x, y, z)
EcsActions.move(dx, dy, dz)
EcsActions.setVelocity(vx, vy, vz)
EcsActions.addVelocity(dvx, dvy, dvz)
EcsActions.setOnGround(true)
EcsActions.setSprinting(true)
EcsActions.removeEntity()
EcsActions.waitTicks(20)              // Wait 20 ticks (1 second at 20 TPS)
EcsActions.waitTicks(20, 60)          // Wait 20 ticks at 60 TPS
EcsActions.modifyComponent(type, component -> { ... })
EcsActions.onEntity(ref -> { ... })

EcsPredicates

Entity state predicates:

EcsPredicates.entityIsValid()
EcsPredicates.hasComponent(componentType)
EcsPredicates.isOnGround()
EcsPredicates.isInAir()
EcsPredicates.isSprinting()
EcsPredicates.atPosition(x, y, z, tolerance)
EcsPredicates.yAbove(100.0)
EcsPredicates.yBelow(50.0)
EcsPredicates.speedAbove(5.0)
EcsPredicates.horizontalSpeedAbove(4.0)
EcsPredicates.movedDistance(startX, startY, startZ, minDistance)
EcsPredicates.component(type, comp -> comp.getValue() > 0)
EcsPredicates.componentValue(type, Component::getValue, v -> v > 0)

EcsRef

Thread-safe entity reference wrapper:

EcsRef<EntityStore> ref = ctx.getEntityRef();

// Execute on world thread (blocking)
ref.execute(r -> {
    Store<EntityStore> store = r.getStore();
    // modify entity
});

// Query on world thread (blocking, returns value)
Double speed = ref.query(r -> {
    Velocity v = r.getStore().getComponent(r, Velocity.getComponentType());
    return Math.sqrt(v.getX()*v.getX() + v.getZ()*v.getZ());
});

// Modify component
ref.modifyComponent(Velocity.getComponentType(), vel -> vel.set(0, 0, 0));

License

MIT

About

Simple library to make integration tests for hytale

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages