How to Debug and Test Unciv: A Complete Guide to Unit Testing and Debugging Flags

Enable debugging flags in DebugUtils.kt and run JUnit tests via ./gradlew :tests:test using the headless GdxTestRunner to verify game logic without a graphical window.

Unciv is an open-source, turn-based strategy game inspired by Civilization V, built with Kotlin and LibGDX. Its architecture cleanly separates pure Kotlin game logic from platform-specific rendering, making it straightforward to debug and test. This guide covers the exact tools and file locations you need to efficiently debug and test Unciv across Android and Desktop builds.

Enable Runtime Debugging with DebugUtils Flags

The core debugging infrastructure lives in core/src/com/unciv/utils/DebugUtils.kt. This file exposes simple mutable Boolean flags that the engine checks at runtime to alter behavior without recompiling the game.

Key Debugging Flags

Toggle these flags during a debugging session to inspect game state:

  • VISIBLE_MAP – Forces the entire world map to render, bypassing fog-of-war logic. Essential for inspecting tile data while the game is running.
  • SHOW_TILE_COORDS – Overlays X/Y coordinates on each tile, helping you locate specific positions when debugging unit movement or AI pathfinding.
  • SHOW_TILE_IMAGE_LOCATIONS – Displays the image source path for each tile graphic, useful for troubleshooting missing or incorrect tilesets.
  • SUPERCHARGED – Skips numerous in-game validation checks to accelerate scenario testing and rapid prototyping of complex game states.
  • SIMULATE_UNTIL_TURN – Automates turn progression up to a specified turn number, perfect for reproducing bugs that only manifest after many turns.

How to Toggle Flags

You can modify these flags directly in the source code before launching, toggle them from the in-game console if available, or adjust them via a settings screen. The world map renderer and other systems check DebugUtils.VISIBLE_MAP and related flags before applying their respective logic.

Run Unit Tests Without a Graphical Window

All pure-Kotlin logic—including game rules, AI, and map generation—is validated by JUnit tests located under tests/src. The project uses a custom headless runner to execute tests without requiring a display.

Understanding the Headless Test Runner

The GdxTestRunner class in tests/src/com/unciv/testing/GdxTestRunner.kt implements ApplicationListener to create a headless LibGDX environment. In its initialization block, it configures a mock OpenGL context:

val conf = HeadlessApplicationConfiguration()
HeadlessApplication(this, conf)               // creates a head-less LibGDX app
Gdx.gl = Mockito.mock(GL20::class.java)       // mocks GL calls
Gdx.gl20 = Gdx.gl

The runner queues each test method to execute during the render() phase where the mock GL context is active. This ensures that any code path touching Gdx.gl runs safely without crashing.

Gradle Test Configuration

The tests/build.gradle.kts file configures the test suite with three critical settings:

  1. Mockito agent – Enables mocking of LibGDX classes (Gdx.gl, etc.) without a graphical context.
  2. workingDir = file("../android/assets") – Points tests to the real game assets (tilesets, translations) required for integration tests.
  3. GdxTestRunner – Applied via @RunWith(GdxTestRunner::class) to provide the headless environment.

Write New Tests for Game Logic

Create test classes under tests/src/com/unciv/logic/ and annotate them with @RunWith(GdxTestRunner::class). The TestGame helper class builds minimal game states for verification.

Here is an example testing promotion logic:

@RunWith(GdxTestRunner::class)
class PromotionAfterTechTest {
    @Test
    fun `Unit gains promotion after researching Gunpowder`() {
        // Build a minimal game with a single unit
        val game = TestGame().apply {
            addUnitAt(TileCoordinate(0, 0), UnitType("Infantry"))
            // Simulate researching the "Gunpowder" tech
            getTechnologyManager().researchTech("Gunpowder")
        }

        // Verify that the unit now has the "Rifleman" promotion
        val unit = game.getUnitAt(TileCoordinate(0, 0))
        assertTrue(unit.promotions.contains("Rifleman"))
    }
}

The headless runner handles GL initialization automatically, allowing you to test game mechanics that depend on the LibGDX environment.

Execute and Filter Tests

From the repository root, use Gradle to run the test suite:


# Execute all tests

./gradlew :tests:test

# Run a specific test class

./gradlew :tests:test --tests *LongPriorityQueueTest

Gradle outputs pass/fail status, standard output captured by GdxTestRunner, and timing data for tests annotated with @MeasureDuration.

Debug Test Failures

When assertions fail, GdxTestRunner automatically prints captured stdout/stderr thanks to its RedirectPolicy.ShowOnFailure logic. For verbose debugging, add println statements directly in your test code or enable the DEBUG flag in your IDE’s run configuration.

Representative test files demonstrating various patterns include LongPriorityQueueTest.kt (pure Kotlin data structures), UnitUniquesTests.kt, EventCircularTriggersTest.kt, and GoldGiftingTests.kt.

Summary

  • DebugUtils.kt provides runtime flags like VISIBLE_MAP and SUPERCHARGED to alter game behavior during debugging sessions.
  • GdxTestRunner creates a headless LibGDX environment with mocked GL contexts, enabling tests to run without a display.
  • Tests reside in tests/src and rely on TestGame helpers to construct minimal reproducible game states.
  • Execute tests via ./gradlew :tests:test and filter specific classes using the --tests flag.
  • The test configuration in tests/build.gradle.kts points to ../android/assets to ensure access to real game data.

Frequently Asked Questions

How do I debug Unciv without playing through dozens of turns manually?

Set the SIMULATE_UNTIL_TURN flag in DebugUtils.kt to automate turn progression up to a specific turn number. This allows you to quickly reach the game state where a bug manifests without manual input.

Why do Unciv tests require a special runner instead of standard JUnit?

Standard JUnit cannot initialize LibGDX classes that expect an OpenGL context. The custom GdxTestRunner creates a HeadlessApplication and mocks GL20 using Mockito, allowing tests to execute code paths that reference Gdx.gl without crashing.

Where should I place new test files in the Unciv repository?

Place new test files under tests/src/com/unciv/ following the package structure of the code being tested. For example, logic tests go in tests/src/com/unciv/logic/ and should use the TestGame helper class to set up game state.

Can I run Unciv unit tests on a continuous integration server without a display?

Yes. The headless test runner requires no graphical environment because it uses HeadlessApplication and mocked GL classes. You can execute ./gradlew :tests:test on headless CI servers as long as the working directory can access the android/assets folder for game data.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →