From fbf5621eaed2528b17934f869875596fe0b7111e Mon Sep 17 00:00:00 2001 From: senior-eng Date: Sun, 27 Sep 2026 10:05:45 -0700 Subject: [PATCH] Adds top-level CMake build (T-04): static JTest lib, example, ctest smoke The project was Makefile-only and non-portable. Adds a top-level CMakeLists.txt so the framework builds with a standard build system: * add_library(JTest STATIC ...) over the 13 src/JTest/*.cpp translation units. No find_package/FetchContent is needed: a full #include scan of include/, src/, examples/ shows the framework depends only on the C++ standard library plus its own headers. * target_include_directories(JTest PUBLIC include) so consumers that link JTest (the example, tests) inherit the include path transitively. * target_compile_features(JTest PUBLIC cxx_std_17) propagates the C++17 requirement (sources use std::optional). * Portable (no -arch) build: the Makefile's non-portable -arch flag is not carried over (that is T-20's scope). * enable_testing() + add_test() running tests/smoke.cpp, a genuine self-check that exercises the public API and exits non-zero on any failed assertion, so ctest is a real pass/fail gate. The demo example remains a build/run target but is intentionally excluded from the CTest gate (it contains deliberate failing/skipping cases to showcase reporting and always exits 0). Verified end-to-end (CMake 4.4.3, AppleClang 21, out-of-source build): configure OK, builds libJTest.a + example + test_smoke, ctest passes (smoke: Passed, 100%), example demo runs. --- CMakeLists.txt | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/smoke.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 tests/smoke.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b140994 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.14) + +project(JTest + VERSION 0.1.0 + DESCRIPTION "A minimal, header-light Jasmine-style C++ test framework (no external dependencies)." + LANGUAGES CXX) + +# --------------------------------------------------------------------------- +# Static library +# The framework has NO external dependency: it only includes C++ standard +# library headers plus its own headers (verified by a full +# #include scan of include/, src/, examples/). No find_package/FetchContent is +# needed. +# +# JTEST_SOURCES is globbed with CONFIGURE_DEPENDS so newly added translation +# units are picked up automatically; the build dir does the heavy lifting. +# JTest is PUBLIC on its include dir so that anything linking it inherits the +# include path transitively -- the demo executable and tests rely on this. +# --------------------------------------------------------------------------- +file(GLOB JTEST_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/JTest/*.cpp") + +add_library(JTest STATIC ${JTEST_SOURCES}) + +target_include_directories(JTest PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") + +# Require C++17 (the sources use std::optional etc.) for JTest's sources and +# propagate the requirement to any target that links JTest. +target_compile_features(JTest PUBLIC cxx_std_17) + +# --------------------------------------------------------------------------- +# Examples +# Build the demonstration as an executable, but do NOT register it with CTest: +# examples/example.cpp deliberately contains failing/skipping cases to show +# JTest's reporting output and always returns 0, so a CTest gate on it would +# be noise. Keep it a normal target so it still builds and runs. +# --------------------------------------------------------------------------- +add_executable(example examples/example.cpp) +target_link_libraries(example PRIVATE JTest) + +# --------------------------------------------------------------------------- +# Testing +# enable_testing() + add_test() wire a genuine self-check that actually +# passes. tests/smoke.cpp exercises the public API (describe/it/expect/ +# execute) and returns non-zero on any failed assertion, so `ctest` is a real +# pass/fail gate: a broken or misconfigured build goes red, not green. +# --------------------------------------------------------------------------- +enable_testing() + +add_executable(test_smoke tests/smoke.cpp) +target_link_libraries(test_smoke PRIVATE JTest) + +add_test(NAME smoke COMMAND test_smoke) diff --git a/tests/smoke.cpp b/tests/smoke.cpp new file mode 100644 index 0000000..fb8176d --- /dev/null +++ b/tests/smoke.cpp @@ -0,0 +1,53 @@ +// Smoke test for the JTest build system. +// +// This is a *genuine* self-check (not a demo). It exercises the public API the +// same way a user program would (describe/it/expect/toEqual/execute) and drives +// `ctest` so a broken or misconfigured build goes red. Unlike +// examples/example.cpp (which intentionally contains failing/skipping cases to +// showcase JTest's reporting and always returns 0), this test returns a +// non-zero exit code when an assertion fails, so CTest produces a real +// pass/fail. +// +// It links JTest and compiles without any external dependency, proving that the +// library archives, the include directory is exported transitively, and C++17 +// is in effect. +#include + +#include + +namespace { +using JTest::describe; +using JTest::execute; +using JTest::expect; +using JTest::it; +using JTest::TestBundle; +using JTest::TestResults; +using std::string; + +int add(int a, int b) { + return a + b; +} +} // namespace + +int main() { + TestResults results = execute( + describe("JTest smoke", []() { + return TestBundle( + { + it("executes a passing arithmetic assertion", []() { + expect(add(2, 2)).toEqual(4); + }), + it("executes a passing string assertion", []() { + expect(string("JTest")).toEqual(string("JTest")); + }), + it("executes a second passing arithmetic assertion", []() { + expect(add(10, 20)).toEqual(30); + }), + }, + JTest::DescribeOptions()); + })); + + // Non-zero exit when any test failed or errored, so CTest surfaces a real + // failure instead of silently passing. + return results.failed == 0 ? 0 : 1; +}