bump to 0.9.8; add padded-canary build (app_version_padding_tests)
Canary re-proven on this base: reconstruct-from-components trips the fixture checks; an unpadded fixture trips the non-vacuity assertion. De-literalizes stale version examples in comments.
This commit is contained in:
+68
-8
@@ -4,15 +4,34 @@ cmake_minimum_required(VERSION 3.19)
|
||||
# Version — SINGLE SOURCE OF TRUTH (Phase V, V1). Edit REASAMPLER_VERSION here and
|
||||
# nowhere else: it flows to the binary constant, the ext-state writing-version stamp,
|
||||
# and the "show version" action via a configure_file'd header (below). The string is
|
||||
# authoritative verbatim — leading zero preserved (Daniel-fixed: exactly "0.9.01",
|
||||
# two-digit zero-padded patch). We deliberately do NOT reconstruct the display string
|
||||
# from project(VERSION)'s numeric components, since CMake may normalize a numeric patch
|
||||
# field; the string variable is what renders. project(VERSION ...) is still set (with a
|
||||
# normalized 0.9.1 triple) for CMake hygiene / any downstream numeric use, but it is NOT
|
||||
# the rendered source of truth.
|
||||
set(REASAMPLER_VERSION "0.9.01")
|
||||
# authoritative verbatim — any leading zeros are preserved exactly as written (Daniel's
|
||||
# ruling: padded and unpadded versions are BOTH legitimate — "0.9.8" and "0.9.80" are
|
||||
# different versions, and the system must never force zero-padding). We deliberately do
|
||||
# NOT reconstruct the display string from project(VERSION)'s numeric components, since
|
||||
# CMake may normalize a numeric patch field; the string variable is what renders.
|
||||
# project(VERSION ...) is still set for CMake hygiene / any downstream numeric use, but
|
||||
# it is NOT the rendered source of truth.
|
||||
#
|
||||
# *** INVARIANT — DO NOT COLLAPSE OR DERIVE ***
|
||||
# The set(REASAMPLER_VERSION ...) line below MUST remain a verbatim string literal,
|
||||
# even when it is textually identical to the project(VERSION ...) line. It must NEVER
|
||||
# become
|
||||
# set(REASAMPLER_VERSION "${PROJECT_VERSION}")
|
||||
# set(REASAMPLER_VERSION "${CMAKE_PROJECT_VERSION}")
|
||||
# or any other form that derives the string from project(). CMake normalizes numeric
|
||||
# patch fields: a padded "0.9.01" would silently round-trip to "0.9.1" and break the
|
||||
# displayed version string. The verbatim-threading invariant is guarded at build time
|
||||
# by app_version_padding_tests (a synthetic zero-padded canary; see the padding-canary
|
||||
# block after app_version_tests). That guard detects reconstruct-from-components
|
||||
# regressions in app_version.cpp but does NOT guard against this line being changed to
|
||||
# a CMake variable derivation — that is this comment's job.
|
||||
#
|
||||
# There is a SECOND set(REASAMPLER_VERSION ...) inside the padding-canary function
|
||||
# further down. That one is a permanent test fixture ("0.9.01") and must NEVER be
|
||||
# bumped on release.
|
||||
set(REASAMPLER_VERSION "0.9.8")
|
||||
|
||||
project(reaper_reasampler VERSION 0.9.1 LANGUAGES CXX)
|
||||
project(reaper_reasampler VERSION 0.9.8 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
@@ -584,6 +603,47 @@ add_executable(app_version_tests tests/test_app_version.cpp)
|
||||
target_link_libraries(app_version_tests PRIVATE app_version)
|
||||
add_test(NAME app_version_tests COMMAND app_version_tests)
|
||||
|
||||
# Anti-normalization padding canary (see tests/test_app_version_padding.cpp for the full
|
||||
# rationale). The live-version assertions in app_version_tests can only DETECT a
|
||||
# "reconstruct the string from numeric components" regression when the live version
|
||||
# happens to be zero-padded — at an unpadded version (e.g. 0.9.8) the reconstruction
|
||||
# coincides with the verbatim string and the check silently goes vacuous. So: run the
|
||||
# SAME version_generated.h.in template through configure_file a second time with a
|
||||
# SYNTHETIC padded version, and compile the SAME src/app_version.cpp against that header
|
||||
# (include-dir substitution — the canary target never sees the live generated/ dir).
|
||||
# The "0.9.01" here is a permanent test fixture, NOT the shipped version (see also the
|
||||
# source-of-truth comment at the top of this file for the live-version invariant); it
|
||||
# must match the literals in test_app_version_padding.cpp and must never be bumped on
|
||||
# release.
|
||||
#
|
||||
# COVERAGE BOUNDARY: the canary detects "reconstruct-from-numeric-components"
|
||||
# regressions inside app_version.cpp. It does NOT detect the live source-of-truth line
|
||||
# becoming a CMake variable derivation; that case is separately guarded by the comment
|
||||
# block at the top of this file.
|
||||
function(_configure_padding_canary)
|
||||
# set() inside a function is function-scoped — the clobber cannot leak into the
|
||||
# parent scope, so no save/restore/unset dance is needed. REASAMPLER_CHANNEL_IS_BETA
|
||||
# and CMAKE_CURRENT_SOURCE_DIR / CMAKE_CURRENT_BINARY_DIR are inherited read-only.
|
||||
set(REASAMPLER_VERSION "0.9.01")
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/version_generated.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_padding_canary/version_generated.h
|
||||
@ONLY)
|
||||
endfunction()
|
||||
_configure_padding_canary()
|
||||
|
||||
# NOTE: app_version_padding_tests deliberately recompiles src/app_version.cpp rather
|
||||
# than linking the app_version library target. This is required for the include-dir
|
||||
# substitution to work — the canary needs to see generated_padding_canary/ instead of
|
||||
# the live generated/ dir. If app_version ever gains a link dependency (e.g. a new
|
||||
# pure-module link), the canary target_link_libraries must mirror it here.
|
||||
add_executable(app_version_padding_tests
|
||||
tests/test_app_version_padding.cpp
|
||||
src/app_version.cpp)
|
||||
target_include_directories(app_version_padding_tests PRIVATE
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_padding_canary src)
|
||||
add_test(NAME app_version_padding_tests COMMAND app_version_padding_tests)
|
||||
|
||||
# The provenance test links bank_model too — it proves the recorded recipe survives
|
||||
# the Sample-JSON round-trip (Provenance.fxChainSnapshot), the M1 seam M10 rides on.
|
||||
add_executable(provenance_tests tests/test_provenance.cpp)
|
||||
|
||||
+21
-14
@@ -21,11 +21,17 @@
|
||||
// PURE MODULE: NO REAPER types, NO SWELL, NO vendor/ includes. Standard library
|
||||
// only. Builds and unit-tests without REAPER (mirror of bank_model / tail_control).
|
||||
//
|
||||
// Leading-zero fidelity (V1, Daniel-fixed): the displayed/stamped string is EXACTLY
|
||||
// "0.9.01" — two-digit zero-padded patch. That exactness is why the version STRING is
|
||||
// the authoritative artifact (sourced verbatim from the one CMake variable), not a
|
||||
// Leading-zero fidelity (V1, Daniel-fixed): the displayed/stamped string preserves the
|
||||
// configured version EXACTLY as written — padded and unpadded versions are both
|
||||
// legitimate (e.g. "0.9.8" and "0.9.80" are different versions; a "0.9.01" renders its
|
||||
// zero-padded patch verbatim). That exactness is why the version STRING is the
|
||||
// authoritative artifact (sourced verbatim from the one CMake variable), not a
|
||||
// reconstruction from numeric components — CMake's `project(VERSION)` may normalize a
|
||||
// numeric patch field, so we never round-trip the string through integers to render it.
|
||||
// Guarded against reconstruct-from-components regressions in app_version.cpp by
|
||||
// app_version_padding_tests (the padded canary build). The canary does NOT guard
|
||||
// against the CMakeLists.txt source-of-truth line being changed to a CMake variable
|
||||
// derivation — that case is guarded by the comment block at the top of CMakeLists.txt.
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -48,20 +54,21 @@ Channel channel();
|
||||
// True on the beta build only. Convenience over channel() == Channel::Beta.
|
||||
bool isBeta();
|
||||
|
||||
// The user-visible version render. Stable: EXACTLY the CMake string ("0.9.01"). Beta:
|
||||
// that string plus a plain "-beta" suffix ("0.9.01-beta") — a plain suffix, NOT a
|
||||
// git-describe decoration (V2, Daniel-fixed). This is what the show-version action and
|
||||
// the bank-panel readout display. It is NOT the ext-state stamp value (see stampVersion).
|
||||
// The user-visible version render. Stable: EXACTLY the configured CMake string. Beta:
|
||||
// that string plus a plain "-beta" suffix — a plain suffix, NOT a git-describe
|
||||
// decoration (V2, Daniel-fixed). This is what the show-version action and the
|
||||
// bank-panel readout display. It is NOT the ext-state stamp value (see stampVersion).
|
||||
const std::string& appVersion();
|
||||
|
||||
// The ext-state STAMP value — the writing-version recorded into a saved project. This is
|
||||
// the NUMERIC TRIPLE ONLY ("0.9.01") on BOTH channels: it deliberately carries NO channel
|
||||
// suffix, so (a) parseVersion classifies it as Stamped when its own channel reads it back
|
||||
// (a "-beta"-suffixed stamp would classify as Unknown — the V4 stamp-classifiability
|
||||
// requirement), and (b) stable's stamp value is byte-identical regardless of the channel
|
||||
// build. The channel is carried by the ISOLATED namespace (see extStateNamespace), never
|
||||
// baked into the stamp. Distinct from appVersion() precisely so the display can say
|
||||
// "-beta" while the stamp stays classifiable and stable-identical.
|
||||
// the NUMERIC TRIPLE ONLY (the configured string, no channel suffix) on BOTH channels:
|
||||
// it deliberately carries NO channel suffix, so (a) parseVersion classifies it as
|
||||
// Stamped when its own channel reads it back (a "-beta"-suffixed stamp would classify
|
||||
// as Unknown — the V4 stamp-classifiability requirement), and (b) stable's stamp value
|
||||
// is byte-identical regardless of the channel build. The channel is carried by the
|
||||
// ISOLATED namespace (see extStateNamespace), never baked into the stamp. Distinct from
|
||||
// appVersion() precisely so the display can say "-beta" while the stamp stays
|
||||
// classifiable and stable-identical.
|
||||
const std::string& stampVersion();
|
||||
|
||||
// The project ext-state namespace this channel reads and writes. Stable: "reasampler"
|
||||
|
||||
+4
-3
@@ -740,9 +740,10 @@ void drawFooter(LICE_IBitmap* bmp, int w, int h) {
|
||||
}
|
||||
|
||||
// Version/channel readout (Phase V, V3/V4), right-aligned, unobtrusive. appVersion()
|
||||
// renders "0.9.01" on stable and "0.9.01-beta" on beta so a beta panel self-identifies.
|
||||
// It sits inside the space footer_bar reserves at the right (rightReserve) and clears the
|
||||
// prune button (prune_button::rightInset). Dim, passive identification (V3).
|
||||
// renders the configured version string on stable and that string plus "-beta" on beta,
|
||||
// so a beta panel self-identifies. It sits inside the space footer_bar reserves at the
|
||||
// right (rightReserve) and clears the prune button (prune_button::rightInset). Dim,
|
||||
// passive identification (V3).
|
||||
kitText(bmp, KitBox{f.left, f.top, (f.right - f.left) - 8, f.bottom - f.top},
|
||||
reasampler::appVersion().c_str(), Font::Micro, Role::TextDim, Align::Right);
|
||||
|
||||
|
||||
@@ -66,8 +66,9 @@ BEGIN_FACTORY(reasampler::vst::kVendorName, reasampler::vst::kVendorUrl,
|
||||
// as literals. DEF_CLASS2 expands inside GetPluginFactory() and PClassInfo2's constructor
|
||||
// copies the char* into its own fixed buffer at that runtime call, so .c_str() on the
|
||||
// accessors' static-storage strings is valid (no dangling — the refs outlive the copy).
|
||||
// vstPluginName(): "ReaSampler 9000" / "ReaSampler 9000 beta". appVersion(): "0.9.01" /
|
||||
// "0.9.01-beta" (the -beta render V4 already yields on beta).
|
||||
// vstPluginName(): "ReaSampler 9000" / "ReaSampler 9000 beta" (live literals in
|
||||
// app_version.cpp). appVersion(): the configured version string / that string plus
|
||||
// "-beta" (the -beta render V4 already yields on beta).
|
||||
DEF_CLASS2(INLINE_UID(REASAMPLER_ACTIVE_UID_1, REASAMPLER_ACTIVE_UID_2,
|
||||
REASAMPLER_ACTIVE_UID_3, REASAMPLER_ACTIVE_UID_4),
|
||||
Steinberg::PClassInfo::kManyInstances, // cardinality
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// test_app_version_padding.cpp — the ALWAYS-ON anti-normalization guard for the version
|
||||
// string threading (Phase V, V1 leading-zero fidelity). No REAPER, no framework.
|
||||
//
|
||||
// WHY THIS EXECUTABLE EXISTS: test_app_version.cpp pins stampVersion() relative to the
|
||||
// live configure_file'd version string — but a live-version assertion can only DETECT a
|
||||
// "reconstruct the string from numeric components" regression when the live version
|
||||
// happens to be zero-padded. "0.9.01" reconstructed via integers renders "0.9.1" and
|
||||
// trips it; "0.9.8" reconstructs to itself, so at an unpadded version the check passes
|
||||
// whether or not the code regressed — the guard silently goes vacuous while green.
|
||||
// Padded and unpadded versions are both legitimate (Daniel's ruling), so the guard must
|
||||
// not depend on the shipped version's shape.
|
||||
//
|
||||
// MECHANISM: CMakeLists.txt runs the SAME src/version_generated.h.in template through
|
||||
// configure_file a second time with a SYNTHETIC zero-padded version ("0.9.01") into
|
||||
// generated_padding_canary/, and this executable compiles the SAME src/app_version.cpp
|
||||
// against that header (include-dir substitution — this target never sees the live
|
||||
// generated/ dir). The exact production derivation path — template substitution →
|
||||
// REASAMPLER_VERSION_STRING → stampVersion()/appVersion() — is thereby exercised with an
|
||||
// input whose normalized form provably differs from its verbatim form. If anyone
|
||||
// reimplements the version string by reconstructing it from numeric components (a
|
||||
// parse-and-reprint inside app_version.cpp, or project(VERSION) numeric macros added to
|
||||
// the template), this binary renders "0.9.1" and the checks below fail — at ANY live
|
||||
// version, which is exactly the property the live-version assertion cannot provide.
|
||||
//
|
||||
// The "0.9.01" literals below are the synthetic fixture and MUST match the canary
|
||||
// configure_file input in CMakeLists.txt. They are NOT the shipped version: never bump
|
||||
// them on release — their padded shape is the entire point.
|
||||
|
||||
#include "../src/app_version.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
using namespace reasampler;
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(cond) do { if(!(cond)) { \
|
||||
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
|
||||
|
||||
static void testPaddedVersionSurvivesThreadingVerbatim() {
|
||||
// The verbatim-threading invariant, exercised with an input where reconstruction and
|
||||
// verbatim DIFFER: a normalized render would be "0.9.1" and fail both checks.
|
||||
CHECK(stampVersion() == "0.9.01");
|
||||
if (isBeta()) CHECK(appVersion() == "0.9.01-beta");
|
||||
else CHECK(appVersion() == "0.9.01");
|
||||
}
|
||||
|
||||
static void testPaddedStampStillClassifiesStamped() {
|
||||
// A padded stamp stays classifiable on read-back: parse tolerates leading zeros
|
||||
// (raw preserved verbatim, triple normalized to integers — {0,9,1}).
|
||||
WritingVersion wv = classifyWritingVersion(stampVersion());
|
||||
CHECK(wv.kind == WritingVersion::Kind::Stamped);
|
||||
CHECK(wv.raw == "0.9.01");
|
||||
CHECK(wv.parsed.major == 0 && wv.parsed.minor == 9 && wv.parsed.patch == 1);
|
||||
}
|
||||
|
||||
static void testCanaryFixtureIsNonVacuous() {
|
||||
// Self-check: the canary fixture must be zero-padded so that its verbatim string
|
||||
// differs from what a parse-and-reprint of its numeric triple would produce.
|
||||
// If the fixture is ever "fixed" to an unpadded form (e.g. "0.9.1"), the checks in
|
||||
// testPaddedVersionSurvivesThreadingVerbatim become vacuous — a reconstructed
|
||||
// string would match the fixture and the canary would pass even if the threading
|
||||
// regressed. Parse stampVersion() and reprint the triple; they must NOT match.
|
||||
WritingVersion wv = classifyWritingVersion(stampVersion());
|
||||
CHECK(wv.kind == WritingVersion::Kind::Stamped); // fixture must be parseable
|
||||
const std::string reprinted =
|
||||
std::to_string(wv.parsed.major) + "." +
|
||||
std::to_string(wv.parsed.minor) + "." +
|
||||
std::to_string(wv.parsed.patch);
|
||||
// If this fails, the fixture is unpadded and the canary guards nothing.
|
||||
// Restore a zero-padded fixture (e.g. "0.9.01") in CMakeLists.txt and
|
||||
// update the matching literals in this file.
|
||||
CHECK(reprinted != stampVersion());
|
||||
}
|
||||
|
||||
int main() {
|
||||
testPaddedVersionSurvivesThreadingVerbatim();
|
||||
testPaddedStampStillClassifiesStamped();
|
||||
testCanaryFixtureIsNonVacuous();
|
||||
|
||||
if (g_fail == 0) std::printf("app_version_padding: all tests passed\n");
|
||||
else std::printf("app_version_padding: %d CHECK(s) FAILED\n", g_fail);
|
||||
return g_fail == 0 ? 0 : 1;
|
||||
}
|
||||
Reference in New Issue
Block a user