Thanks for your interest in improving Bloviate! This guide covers how to build the project locally, run the test suite, and submit changes.
- Java 25 or higher (the build targets
--release 25) - Maven — use the bundled wrapper (
./mvnw); no separate install required - Docker — required to run the integration tests, which spin up real databases via TestContainers. OrbStack or Docker Desktop both work.
Bloviate is a multi-module Maven build. The root pom.xml is the parent (packaging pom); the
code lives in modules:
| Module | Description |
|---|---|
bloviate-core |
The self-contained data-generation engine and flat-file support |
bloviate-junit |
JUnit Jupiter integration (@FillDatabase); JUnit is a provided dependency |
bloviate-testcontainers |
Testcontainers integration; Testcontainers is a provided dependency |
Shared dependency and plugin versions are managed centrally in the parent pom.xml. ./mvnw
commands run from the repository root build the whole reactor.
# Compile the project
./mvnw compile
# Compile, run all tests, and verify
./mvnw verify
# Package the JAR
./mvnw package
# Clean and recompile from scratch
./mvnw clean compileAll of the following are part of ./mvnw verify; there is no separate command to
remember. The first two run at validate, before anything is compiled, so a problem
fails in seconds rather than after the integration suite has started containers.
| Check | Phase | Enforces | Fails the build |
|---|---|---|---|
maven-enforcer-plugin |
validate |
Maven [3.9.0,), Java [25,), no duplicated dependency versions, dependency convergence |
yes |
spotless-maven-plugin |
validate |
Apache-2.0 license header on every .java file, no trailing whitespace, newline at EOF |
yes |
spotbugs-maven-plugin |
verify |
Bytecode analysis, effort=Max, threshold=Medium |
yes |
maven-pmd-plugin (PMD) |
verify |
Source analysis against a curated ruleset | yes |
maven-pmd-plugin (CPD) |
verify |
Copy-paste blocks of 100+ tokens | yes |
jacoco-maven-plugin |
verify |
Per-package line/branch coverage floors | yes |
Shared configuration lives at the repository root so all five modules use one copy:
config/pmd/ruleset.xml PMD rules and the exclusions, each with its rationale
config/spotbugs/exclude.xml SpotBugs suppressions, each with its rationale
license-header.txt the canonical Apache-2.0 header
Every check above now gates the build; none are advisory. Counts still appear in the build output and the CI job summary, so a regression is visible before it is fatal.
When adding a suppression, scope it as narrowly as the finding allows and say why it is safe. A pattern suppressed repository-wide hides the next genuine instance of it. Prefer, in order:
- Fix the code, when the tool is right.
@SuppressWarnings("PMD.RuleName")on the smallest enclosing element, with a comment giving the reason — this keeps the rule live everywhere else.- A ruleset exclusion, only when the pattern is deliberate across the whole codebase (a builder convention, an SQL NULL fidelity requirement) rather than local to one method.
Two traps worth knowing when editing config/pmd/ruleset.xml:
- An
<exclude>in the wrong category block silently does nothing. Rules live in specific categories, and excludingFoofrombestpracticeswhen it belongs toerrorproneis a no-op — the build still passes and the report still generates. PMD logsExclude pattern 'Foo' did not match any rule in ruleset '...', so grep the build output fordid not match any ruleafter every ruleset edit. - XML comments cannot contain
--. A comment mentioningi--or--flagmakes the ruleset unparseable, and the resulting failure names a generated file undertarget/pmd/rulesets/, not the file you edited.
If Spotless reports a violation, fix it automatically:
./mvnw spotless:applyThe canonical header lives in license-header.txt at the repository root; new source
files must start with it verbatim. Spotless is deliberately configured for headers and
whitespace only — it does not impose a Java formatter, so existing code layout and
git blame history are left alone.
Dependency convergence is enforced because Bloviate is not shaded: every transitive
version is one a consumer actually inherits. When a new dependency introduces a
conflict, resolve it with an explicit dependencyManagement pin in the parent
pom.xml rather than relying on Maven's nearest-wins tiebreak.
In CI a dedicated static-analysis job runs these checks without the integration
tests, so a formatting or analysis problem reports in about a minute rather than
waiting on Docker. The reports (spotbugsXml.xml, pmd.xml, cpd.xml) are uploaded
as build artifacts, and a summary table appears on the workflow run page.
A CycloneDX SBOM is generated at package. Each module gets its own target/bom.xml
covering just that module's dependencies, and the reactor root gets an aggregate; both
are attached to the build so releases carry a component inventory.
The project uses TestContainers for integration testing against real databases, so Docker must be running.
# Run all tests
./mvnw test
# Run a specific test class
./mvnw test -Dtest=PostgresFillerTest
# Run database-specific integration tests
./mvnw test -Dtest=PostgresFillerTest
./mvnw test -Dtest=MySqlFillerTest
./mvnw test -Dtest=CockroachDBFillerTestTest schemas live under bloviate-core/src/test/resources/ (TPCC, AuctionMark, Wikipedia, and
others). BaseDatabaseTestCase provides the shared DataSource plumbing, the classpath script
runner, and the fidelity assertions used by the TPC-C tests.
BigQueryFillerTest is the one test Docker cannot cover. BigQuery has no usable emulator — the
tbc-bq-jdbc driver deliberately removed its emulator tier because the emulator diverged far enough
from the service to hide real defects — so the test needs a live Google Cloud project, and it is
skipped by default.
It is gated twice, and both gates must pass:
BLOVIATE_BQ_PROJECTis set (with Application Default Credentials available), and- the driver is on the classpath, which only happens under
-Pbigquery.
The second gate exists so that setting the env var without the profile skips cleanly instead of
failing with "No suitable driver". vc.tbc:tbc-bq-jdbc is not on Maven Central yet, which is why it
is declared in an opt-in profile rather than as an ordinary test dependency — a default build must
stay resolvable for everyone. Bump tbc-bq-jdbc.version in the root POM by hand; Dependabot and
versions:display-dependency-updates cannot resolve that coordinate.
# once, in a clone of https://github.com/Two-Bear-Capital/tbc-bq-jdbc
./mvnw clean install
gcloud auth application-default login
export BLOVIATE_BQ_PROJECT=my-gcp-project
./mvnw verify -Pbigquery -pl bloviate-core -Dtest=BigQueryFillerTestEach run creates its own dataset and drops it afterwards (with a one-day default table expiration as
a backstop), because DatabaseFiller fills every table it finds in the connection's schema.
Running it writes real data and runs real jobs, both of which cost money.
Integration tests rely on Testcontainers, which starts and tears down database containers automatically — a running Docker daemon is the only prerequisite. There is nothing to start or stop by hand.
Every image tag is pinned to an explicit version — never :latest. A floating tag lets a
database release change test behaviour with no commit to this repository, so a red build has no
diff to bisect and a CI failure may not reproduce locally once the tag has moved on. That cuts
against the seed-reproducibility invariant below.
Tags are declared in two places, not scattered across test classes:
| File | Covers |
|---|---|
bloviate-core/src/test/java/io/bloviate/db/TestImages.java |
every bloviate-core integration test |
bloviate-benchmarks/src/test/java/io/bloviate/bench/BenchImages.java |
the end-to-end fill benchmarks |
Two single-use literals remain, in bloviate-junit and bloviate-testcontainers — a constants
class for one reference would be noise. Sharing one class across all four modules would mean
publishing a test-jar from bloviate-core, which is disproportionate for four strings.
So bumping a database version means editing TestImages, BenchImages, and those two literals.
Find them all with:
grep -rn --include='*.java' -E '"(postgres|mysql|mariadb|cockroachdb/cockroach):' bloviate-*/srcKeep TestImages and BenchImages in step: a benchmark measuring a different database version
than the tests exercise produces numbers that can't be compared against them.
A few properties are hard guarantees:
- Seed reproducibility within a version. For a given Bloviate version, the same schema filled with the same seed must produce byte-for-byte identical data on every run, on every platform and JDK. Nothing that feeds generation may depend on run-to-run or JDK-dependent state (hash iteration order, wall-clock time, identity hash codes, default locale/timezone).
- Cross-version changes are allowed but must be deliberate. A new release may change the data a
seed produces (e.g. a fixed traversal order or an improved generator), but the change must be
intentional, called out in the release notes, and accompanied by regenerating the golden dump in
SeedGoldenDumpTest— never an accidental side effect of a refactor. The golden-dump test exists to turn any unintentional drift into a loud failure.
- Fork the repository
- Create a feature branch (
git checkout -b feat/amazing-feature) - Make your changes, following the commit message format below
- Ensure
./mvnw verifypasses (tests included) - Push to your fork (
git push origin feat/amazing-feature) - Open a Pull Request
Note: PR titles are validated by commitlint and must follow the Conventional Commits format described below.
This project uses Conventional Commits to drive automatic semantic versioning via semantic-release:
<type>(<scope>): <description>
[optional body]
[optional footer]
Commit types and version impact:
| Type | Version bump |
|---|---|
feat: |
Minor (new feature) |
fix: |
Patch (bug fix) |
perf: |
Patch (performance) |
refactor: |
Patch (refactor) |
feat!: or BREAKING CHANGE: |
Major |
docs:, style:, test:, ci:, chore: |
None |
Examples:
git commit -m "feat(database): add connection pooling support"
git commit -m "fix(generator): resolve null pointer in StringGenerator"
git commit -m "docs: update installation instructions"- Follow existing code style and conventions
- Add tests for new features
- Update documentation as needed
- Ensure all tests pass (
./mvnw verify) before submitting a PR - Use conventional commit messages — and a conventional PR title — for automatic versioning
- Issues: GitHub Issues
- Discussions: GitHub Discussions