Add POST coverage for physics-freeze words (Module 27), fix two real bugs found in the process

Cluster 4 of the POST-coverage sweep: physics_freeze_words_test.c covers the 6
words proof/StarForth_Physics_Freeze_Words.thy actually gives real lemmas for
(FREEZE-WORD, UNFREEZE-WORD, FROZEN?, HEAT!, HEAT@, DECAY-RATE@), correcting
an earlier fork summary's wrong "5 words" scope.

Writing the tests surfaced two independent, pre-existing bugs in
physics_freeze_words.c, both now fixed:

- Every address-taking word cast the VM's caddr directly to a host pointer
  instead of resolving it through vm_ptr() -- caddr is an offset into
  vm->memory, not a host pointer. Fixed in all 9 call sites (the 5 in-scope
  words plus SHOW-HEAT, which shares the identical pattern).
- Every underflow check used dsp < N (item count) instead of dsp < N-1, since
  this VM's dsp is a 0-indexed top-of-stack pointer. Fixed in all 6 checks.

Together these meant every word in this file taking a stack-supplied name has
been broken for any real caller since the file was written. Verified zero
build warnings and a clean three-arch QEMU boot (amd64/aarch64/riscv64), 1009
passed / 0 failed / 0 errors identically on all three, dict_hash matching
across arches.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-19 00:12:09 -04:00
co-authored by Claude Sonnet 5
parent 825ab078f1
commit abb858a300
15 changed files with 168852 additions and 78762 deletions
+6
View File
@@ -115,6 +115,12 @@ void run_q48_words_tests(VM * vm);
*/
void run_inference_words_tests(VM * vm);
/*
* @brief Run tests for the proof-covered subset of physics-freeze words (Module 27)
* @param vm Pointer to the VM instance
*/
void run_physics_freeze_words_tests(VM * vm);
/*
* @brief Run tests for Mama FORTH vocabulary (capsule system M7.1)
* @param vm Pointer to the VM instance
@@ -0,0 +1,127 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 20232025 Robert A. James
All rights reserved.
This file is part of the StarForth project.
Licensed under the StarForth License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
express or implied, including but not limited to the warranties of
merchantability, fitness for a particular purpose, and noninfringement.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
* physics_freeze_words_test.c — POST tests for the proof-covered subset of
* physics_freeze_words.c (Module 27)
*
* physics_freeze_words.c registers 9 words; proof/COVERAGE.md and
* proof/StarForth_Physics_Freeze_Words.thy give real (non-"True"-placeholder)
* lemmas for exactly 6 of them: FREEZE-WORD, UNFREEZE-WORD, FROZEN?, HEAT!,
* HEAT@, DECAY-RATE@. FREEZE-CRITICAL, SHOW-HEAT, and ALL-HEATS carry only
* trivial "not_modelled"/pass-through True lemmas, so they are intentionally
* left uncovered here (matches the 6/9 figure in COVERAGE.md). No test file
* for this module existed before.
*
* Each test case defines a scratch dictionary word (`:` ... `;`) and takes
* its name via S" — every word here operates on ( caddr u ), the standard
* counted-string-less pair the rest of the FORTH interpreter uses. Using
* `run_test_suite` (no WordContract) rather than `run_test_suite_m`, matching
* defining_words_tests.c's precedent: these test cases define dictionary
* words, and check_physics_transparent's own contract explicitly documents
* that its re-run mechanism does not snapshot/restore dictionary state.
*/
#include "../include/test_runner.h"
#include "../include/test_common.h"
/* Physics Freeze Words Test Suites - Module 27 */
static WordTestSuite physics_freeze_word_suites[] = {
{
"FREEZE-WORD", {
{"basic",
": __freeze_test_word__ ; S\" __freeze_test_word__\" FREEZE-WORD S\" __freeze_test_word__\" FROZEN? . CR",
"Should print: -1 (word is now frozen)", TEST_NORMAL, 0, 1, {0}},
{NULL, NULL, NULL, TEST_NORMAL, 0, 0, {0}}
},
1, {0}
},
{
"UNFREEZE-WORD", {
{"basic",
": __unfreeze_test_word__ ; S\" __unfreeze_test_word__\" FREEZE-WORD S\" __unfreeze_test_word__\" UNFREEZE-WORD S\" __unfreeze_test_word__\" FROZEN? . CR",
"Should print: 0 (freeze cleared)", TEST_NORMAL, 0, 1, {0}},
{NULL, NULL, NULL, TEST_NORMAL, 0, 0, {0}}
},
1, {0}
},
{
"FROZEN?", {
{"not_frozen",
": __frozen_test_word__ ; S\" __frozen_test_word__\" FROZEN? . CR",
"Should print: 0 (freshly defined word is not frozen)", TEST_NORMAL, 0, 1, {0}},
{"not_found",
"S\" __nonexistent_freeze_xyz__\" FROZEN? . CR",
"Should print: 0 (not-found treated as not frozen)", TEST_EDGE_CASE, 0, 1, {0}},
{NULL, NULL, NULL, TEST_NORMAL, 0, 0, {0}}
},
2, {0}
},
{
"HEAT!", {
{"basic",
": __heat_store_test_word__ ; 999 S\" __heat_store_test_word__\" HEAT! S\" __heat_store_test_word__\" HEAT@ . CR",
"Should print: 999", TEST_NORMAL, 0, 1, {0}},
{NULL, NULL, NULL, TEST_NORMAL, 0, 0, {0}}
},
1, {0}
},
{
"HEAT@", {
{"of_fresh_word",
": __heat_fetch_test_word__ ; S\" __heat_fetch_test_word__\" HEAT@ . CR",
"Should print: 0 (freshly defined word has zero heat)", TEST_NORMAL, 0, 1, {0}},
{NULL, NULL, NULL, TEST_NORMAL, 0, 0, {0}}
},
1, {0}
},
{
"DECAY-RATE@", {
{"basic", "DECAY-RATE@ . CR",
"Should print the compile-time DECAY_RATE_PER_US_Q16 constant (no error)", TEST_NORMAL, 0, 1, {0}},
{NULL, NULL, NULL, TEST_NORMAL, 0, 0, {0}}
},
1, {0}
},
{NULL, {{NULL, NULL, NULL, TEST_NORMAL, 0, 0, {0}}}, 0, {0}}
};
/**
* @brief Executes the proof-covered subset of physics-freeze word test suites
* @param vm Pointer to the Forth virtual machine instance
* @details Covers the 6 words proof/StarForth_Physics_Freeze_Words.thy gives
* real lemmas for: FREEZE-WORD, UNFREEZE-WORD, FROZEN?, HEAT!,
* HEAT@, DECAY-RATE@. Does not cover FREEZE-CRITICAL, SHOW-HEAT, or
* ALL-HEATS — those carry only trivial/not-modelled lemmas in the
* same theory file.
*/
void run_physics_freeze_words_tests(VM *vm) {
log_message(LOG_INFO, "Running Physics Freeze Words Tests (Module 27)...");
for (int i = 0; physics_freeze_word_suites[i].word_name != NULL; i++) {
log_message(LOG_TEST, "▶ Testing module: %s", __FILE__);
run_test_suite(vm, &physics_freeze_word_suites[i]);
}
print_module_summary("Physics Freeze Words", 0, 0, 0, 0);
}
+1
View File
@@ -132,6 +132,7 @@ static TestModule test_modules[] = {
{"Mama FORTH Words", NULL, 0, run_mama_forth_words_tests}, /* Module 24: Capsule System M7.1 */
{"Q48.16 Words", NULL, 0, run_q48_words_tests}, /* Module 25: Q48.16 Fixed-Point Arithmetic */
{"Inference Engine Words", NULL, 0, run_inference_words_tests}, /* Module 26: SSM Inference (proof-covered subset) */
{"Physics Freeze Words", NULL, 0, run_physics_freeze_words_tests}, /* Module 27: Physics Freeze (proof-covered subset) */
{NULL, NULL, 0, NULL} /* End marker */
};