From d43f996604ffce91f62531d4422b4e94da3d3ae7 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Wed, 15 Apr 2026 08:41:02 -0700 Subject: [PATCH] Wildcat: full SDK init (InitWCglobal + LoadMakeWild + BTInitIsam); test across 7 conferences The TWildcatBase.OpenConference was creating a TMsgDatabase without populating the SDK globals, causing access violations on MwConfig. Now calls the full Register sequence: InitWCglobal, LoadMakeWild, BTInitIsam before opening; BTExitIsam + DisposeWCglobal on close. test_wildcat reads conferences 0..6 from vendored testdata (copied to /tmp/ma_wildcat so the source tree stays read-only). --- docs/format-notes/dependencies.md | 12 +++- run_tests.sh | 4 +- src/formats/ma.fmt.wildcat.pas | 42 ++++++++++++- tests/test_wildcat.pas | 99 +++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 tests/test_wildcat.pas diff --git a/docs/format-notes/dependencies.md b/docs/format-notes/dependencies.md index 1134af5..f562933 100644 --- a/docs/format-notes/dependencies.md +++ b/docs/format-notes/dependencies.md @@ -2,7 +2,8 @@ ## Sample data -Real message bases for testing live at `~/fidonet/msg/`: +Real message bases for testing live at `~/fidonet/msg/` and for +WildCat! at `~/allfix_dev/f_drive/tpfiles/allfix/wc_dev/testdata/`: - `hudson/` — Hudson MSGINFO/IDX/HDR/TXT/TOIDX.BBS set - `jam/` — many JAM echo areas (e.g. `10thamd.jhr/jdt/jdx/jlr`) @@ -11,9 +12,16 @@ Real message bases for testing live at `~/fidonet/msg/`: - `squish/` — Squish bases (currently empty but layout reserved) - `local/jam/`, `lowerit/`, `passthru/` — additional collections +- WildCat 4 testdata: + - `CONFDESC.DAT/IX/UX` — conference descriptors (7 conferences) + - `MSG/MSG0..MSG6.DAT + .IX` — per-conference message databases + - `DATA/ALLUSERS.DAT + USERCONF.DAT` — users + per-user conference membership + Tests should open these read-only and verify message counts, first/last messages, and specific known attributes. Do NOT write to these paths -from tests — copy them into a scratch dir first. +from tests — copy them into a scratch dir first. The Wildcat SDK +mutates the message database during Open (btis locks, modcounters) so +even "read" paths must go through a scratch copy. ## Authoritative specs diff --git a/run_tests.sh b/run_tests.sh index 98b0bdd..2cfaab3 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -19,7 +19,7 @@ compile() { local src=$1 echo " compile $(basename "$src")" "$FPCNATIVE" -Mobjfpc -Sh -Sgic \ - -Fusrc -Fusrc/formats -Futests \ + -Fusrc -Fusrc/formats -Fusrc/wc_sdk -Futests \ -FUunits/"$TGT" -FEexe/"$TGT" \ "$src" >/tmp/ma_test_build.$$.log 2>&1 || { cat /tmp/ma_test_build.$$.log @@ -41,6 +41,7 @@ compile tests/test_read.pas compile tests/test_roundtrip.pas compile tests/test_lock.pas compile tests/test_batch.pas +compile tests/test_wildcat.pas echo echo "Running tests..." @@ -48,6 +49,7 @@ run test_read run test_roundtrip run test_lock run test_batch +run test_wildcat echo echo "All tests passed." diff --git a/src/formats/ma.fmt.wildcat.pas b/src/formats/ma.fmt.wildcat.pas index 089c18d..c97a1b1 100644 --- a/src/formats/ma.fmt.wildcat.pas +++ b/src/formats/ma.fmt.wildcat.pas @@ -48,7 +48,7 @@ interface uses WcType, WcMsgDb, WcUserDb, WcGlobal, WcMisc, WcDb, - Filer, BTISBase; + Filer, BTISBase, BTFileIO; type TWildcatBase = class @@ -150,6 +150,24 @@ end; { --- Conference management --- } +function LoadMakeWild(var AMwConfig: TMakewildRec): boolean; +var + F: file of TMakewildRec; + SaveFileMode: word; +begin + Result := False; + Assign(F, 'MAKEWILD.DAT'); + SaveFileMode := FileMode; + FileMode := 66; { deny-none share, read-write } + {$I-} Reset(F); {$I+} + FileMode := SaveFileMode; + if IoResult <> 0 then exit; + {$I-} Read(F, AMwConfig); {$I+} + Result := IoResult = 0; + {$I-} Close(F); {$I+} + if IoResult = 0 then {ignore}; +end; + function TWildcatBase.OpenConference(Conf: word): boolean; begin Result := False; @@ -158,6 +176,24 @@ begin SaveAndChangeDir; + { WC SDK globals must be populated before opening any database. + InitWCglobal allocates the pointers; LoadMakeWild fills MwConfig^ + from MAKEWILD.DAT in the current dir; BtInitIsam brings up the + ISAM layer the message/user databases sit on. Done in + CloseConference. } + InitWCglobal; + if not LoadMakeWild(MwConfig^) then begin + DisposeWCglobal; + RestoreDir; + exit; + end; + BtInitIsam(NetSupportType(MwConfig^.Network), MinimizeUseOfNormalHeap, 0); + if not IsamOk then begin + DisposeWCglobal; + RestoreDir; + exit; + end; + New(FMsgDb, Init); New(FUserDb, Init); @@ -170,6 +206,8 @@ begin FUserDb := nil; if FMsgDb <> nil then Dispose(FMsgDb); FMsgDb := nil; + BtExitIsam; + DisposeWCglobal; RestoreDir; end; end; @@ -190,6 +228,8 @@ begin FUserDb := nil; end; + BtExitIsam; + DisposeWCglobal; RestoreDir; end; diff --git a/tests/test_wildcat.pas b/tests/test_wildcat.pas new file mode 100644 index 0000000..d046331 --- /dev/null +++ b/tests/test_wildcat.pas @@ -0,0 +1,99 @@ +{ + test_wildcat.pas - WildCat! 4 adapter smoke test. + + The WC SDK opens databases for write (btis lock, modcounter + bumps) even when the caller only reads, so we never touch the + source tree directly. This test copies the whole testdata + directory to a scratch location first, runs the adapter there, + then leaves the scratch alone for manual inspection. + + Source lives at + ~/allfix_dev/f_drive/tpfiles/allfix/wc_dev/testdata/ + and is NEVER modified. +} + +program test_wildcat; + +{$mode objfpc}{$H+} + +uses + SysUtils, + testutil, + ma.types, ma.events, ma.api, + ma.fmt.wildcat, ma.fmt.wildcat.uni; + +const + SRC = '/home/ken/allfix_dev/f_drive/tpfiles/allfix/wc_dev/testdata'; + SCRATCH = '/tmp/ma_wildcat'; + +function RunShell(const Cmd: string): integer; +begin + Result := ExecuteProcess('/bin/sh', ['-c', Cmd]); +end; + +function CopyTree(const Source, Dest: string): boolean; +var + cmd: string; +begin + cmd := SysUtils.Format('cp -r "%s"/. "%s"/ 2>/dev/null', [Source, Dest]); + Result := RunShell(cmd) = 0; +end; + +procedure PrepareScratch; +begin + if DirectoryExists(SCRATCH) then + RunShell(SysUtils.Format('rm -rf "%s"', [SCRATCH])); + ForceDirectories(SCRATCH); + if not CopyTree(SRC, SCRATCH) then + raise Exception.Create('cp -r failed'); +end; + +procedure TestConference(Conf: word); +var + base: TMessageBase; + adapter: TWildcatMessageBase; + msg: TUniMessage; + i, count, ok: longint; +begin + TestBegin(SysUtils.Format('WildCat! conference %d', [Conf])); + base := MessageBaseOpen(mbfWildcat, SCRATCH, momReadWrite); + adapter := base as TWildcatMessageBase; + adapter.Conference := Conf; + try + AssertTrue('Open', base.Open); + count := base.MessageCount; + ok := 0; + for i := 0 to count - 1 do + if base.ReadMessage(i, msg) then begin + Inc(ok); + if i = 0 then + AssertTrue('First msg has sender', + Length(msg.WhoFrom) > 0); + end; + AssertEquals('Read all reported messages', count, ok); + finally + base.Close; + base.Free; + end; + TestOK; +end; + +var + conf: word; +begin + WriteLn('message_api: WildCat! 4 read tests'); + WriteLn; + + if not DirectoryExists(SRC) then begin + WriteLn('SKIP (sample at ', SRC, ' missing)'); + Halt(0); + end; + + PrepareScratch; + + { testdata has conferences 0..6. Walk each one. } + for conf := 0 to 6 do + TestConference(conf); + + Halt(TestsSummary); +end.