Repository navigation
Test framework, stage 3: llmessage's and llcorehttp's tests in Catch2 - #455
Merged
Merged
Conversation
… closes one it cannot bind ~LLMessageSystem closed its socket only when the system was OK. But the constructor loads the message template before it opens the socket, so a system whose template failed to load had an open socket and was not OK, and deleting it leaked the socket, and on Windows its WSAStartup. The guard was there because a system whose start_net failed held socket 0, stdin's descriptor, which end_net would have closed. - No socket is -1 now, not 0, and the destructor closes whatever socket it has. end_net closes only a socket that is open, and leaves -1 behind, so a second call does nothing. On Windows it undoes WSAStartup only for a socket start_net opened. - start_net returned without closing its socket when it found no port to bind, or bind failed some other way. Both paths close it now, and on Windows undo WSAStartup, as the path where socket() itself fails did. The viewer never noticed: it quits when its message system fails to start. The tests did. llmessage's test fixture closed the socket itself, working around the leak; it no longer needs to, and with the fix it would have closed the same descriptor twice. message_test checks both. A system made without its template frees its port when it is deleted, so start_net binds that port again rather than moving on into the discovery range. And on POSIX, start_net with every port it would try taken returns 3 and leaves no descriptor open. Without the fix, the first finds the port still taken and is moved to 13000, and the second finds one descriptor more. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hat directory's file LLMessageConfigFile is an LLLiveFile, which watches the one path it was made with, and the file was a function-local static, made on the first initClass. A later initClass with another directory moved where loadFile read from but not what was watched: the new directory's file was read only when the first directory's file changed. The viewer calls initClass once, so it never saw this; llmessage's tests call it from every fixture, each with a directory of its own, and their reload test passed only because the first fixture deleted its file, which counts as a change. The file is held in llmessageconfig.cpp now, and made anew when initClass names another directory, so the file read is the file watched. initClass with the same directory keeps the file, and what it loaded, as before. sServerName, sConfigDir, sServerDefault and sMessages were static variables defined in the header, so every file including it had copies of its own; only llmessageconfig.cpp's were used, and the last two nowhere. The first two move into llmessageconfig.cpp, and the others go. llmessageconfig_test checks it: initClass with a second directory gets that directory's config at once. Without the fix it gets none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…erever a launcher put it record refuses results from an executable built before its source last changed, and took the executable to be the first word of the entry's command. llcorehttp's entry runs its executable under a Python peer, so the first word is the interpreter, and record refused it whenever the source was newer than the interpreter, which is always. The executable is now the word in the command named for the entry's target, INTEGRATION_TEST_<test> for an INTEGRATION_TEST_RUNNER_<test> entry and the entry's own name otherwise, and the first word only where none is. llcorehttp records (83 ok, 5 skipped), and the other entries find what they found before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
llcorehttp's tests were one TUT executable, built from llcorehttp_test.cpp, which #included ten test_*.hpp suites into one translation unit and defined the helpers the HttpRequest suite uses. A suite's results could not be told from another's by the file they ran from, and each suite compiled only because the ones before it had made its names visible. Each suite is now <suite>_test.cpp, an executable of its own run under the Python peer as before, so the stage 3 conversion and its parity check treat them as any other test file: - include guards go, and each file includes linden_common.h and lltut.h first, as the one file did before them; - four suites used LLCore names that an earlier suite's using-directive had brought in, and say so themselves; - init_curl, term_curl, ScopedCurlInit, get_base_url and stop_thread move into httprequest_test.cpp, the only suite that uses them, and llcorehttp_test.cpp and llcorehttp_test.h go. The ten executables report exactly what the one did: the same 88 tests, in the same ten groups, with the same results (83 ok, 5 skipped). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d before naming itself Two things llcorehttp brought out: - check refused results from a converted file newer than its binary, taking the binary to be the first word of the entry's command. A binary run under a launcher, as llcorehttp_tests runs under its Python peer, has the interpreter there. check now finds the binary by name, as record finds a TUT executable, through one helper. - TUT prints a test's name only once its set_test_name has run. Five of HttpRequest's tests skip on this platform before they get there, so TUT printed no name, and check held that against the name the converter read from the set_test_name. Such a test, one that skipped or failed with no name printed, is listed rather than a mismatch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--names keys a name by group and test number, so the two definitions of one test<N> in the branches of an #if, as alnetimpairment's test<1> is with and without AL_NET_IMPAIRMENT, took one name between them, and the idiom pass could not name them apart: the compiled twin was given the other's name. A name may now be a list, one a twin in the order the file declares them; a plain name is still every twin's. Over TM19's 31 files with their names, only alnetimpairment's first case comes out differently, and as its own name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h2 tests alike Sync, which steps coroutines in turn, failed a timed-out wait with tut::fail, and so included lltut.h, which a Catch2 test cannot build with. llmessage's llcoproceduremanager test uses it and is converted in TM19; llcommon's lleventcoro and llmainthreadtask use it and are TUT until TM20. al_add_test_binary defines AL_TEST_CATCH2 for every Catch2 binary, and Sync FAILs where it is set and calls tut::fail where it is not. The TUT branch goes with TUT (TM26). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…one is given llcorehttp's tests run under a Python peer, test_llcorehttp_peer.py, which serves the requests they make and starts the test program itself once its port is known. al_add_test's COMMAND said so; a Catch2 binary had no way to. LAUNCHER names a command each entry runs the binary through, the binary's path and arguments after the command's own: each file's entry and the combined run. stray_cases runs no case, so it runs the binary bare. In per-case mode, Catch.cmake runs the discovery and each case through the target's CROSSCOMPILING_EMULATOR, which CMake's own add_test leaves alone in a native build, so the launcher is set there too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tut2catch.py TM19, the second wave of stage 3: 31 TUT files, 361 tests, become five Catch2 binaries, each file still run in a process of its own: - llmessage_tests, 18 files linking llmessage, with io_test's pipes (llpipeutil.cpp) as an OBJECT library beside them; - llcoproceduremanager_test, lltrustedmessageservice_test and lltemplatemessagedispatcher_test alone: they stub parts of llmessage and llcorehttp, so they compile the file they test in (UNIT); - llcorehttp_tests, its ten suites, run under the Python peer through LAUNCHER as the TUT executables were, with http_proxy cleared. The message-system tests keep the system of their own that stage 1 gave each, on a port the OS picks, rather than one shared for the process, which the plan had proposed: a case's system is gone when it ends, and nothing waits on another's. The files are tut2catch.py's output with the names the idiom pass that follows chose, given through --names; none carries a hand edit. tut_parity.py, against the TUT executables' recorded results (361 tests: 355 ok, 6 skipped), holds for all 31 as the converter wrote them. alnetimpairment's test without AL_NET_IMPAIRMENT is not compiled here, and five HttpRequest tests that skip before naming themselves are listed. All 38 entries pass, the two combined runs among them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The idiom pass over TM19's 31 converted files, as for llmath and TM18: - every case named for what it checks: 233 were #N, and many names taken from set_test_name or comments ran long, carried commas or asked questions; - messages that only restated their assertion are gone: 1,244 became 205, and 24 CAPTUREs say what string-building said before; - asserting helpers are named for what they do now (require_*), with a scoped INFO or CAPTURE in place of a message built by hand; - 111 REQUIREs that guarded nothing are CHECKs, and the 4 CHECKs that guard a read after them are REQUIREs. No case was added, removed or moved, and no assertion: per case, each assertion compares what it compared before, in the same order. Six changed only in form: literals moved right, and an UNSCOPED_INFO and REQUIRE(false) in HttpRequest's handler became a FAIL. tut_parity.py --recounted holds for all 31 files, and all 38 entries pass. Bugs in the tests that the pass found are left for their own change, since a conversion keeps behaviour: - lldatapacker: sizes taken with sizeof of a pointer, so arbitrary; a 31-bit tolerance on a value packed with 23; binary round trips never check the size read back; buffers that leak. - llnamevalue: a value built and never checked, and the read-only VEC3 setter called on the F32 value instead. - lltemplatemessagebuilder: the "unchanged buffer" case clears the wire buffer and never the output; a size read after the index it guards; both quaternion round trips use angle 0, the identity. - llsdmessagebuilder: binary cases that pass a size a fraction of the array; an S16 array compared for half its bytes; guards that check size > 0 before reading N bytes; an empty case; fixtures that leak. - io and llbuffer: iterators dereferenced after ++ with no check against endSegment(); one write a byte past a full buffer; comments that say the opposite of their check (llbuffer, llxorcipher). - llhttpnode: a node addNode refuses is leaked. - lltemplatemessagedispatcher: every case dereferences a null reader. - httpstatus: status1 assigned twice, where status2 was meant. - bufferarray and bufferstream: a guard-byte check one byte late, and a pbackfail case whose expectation reads backwards. - message_test: Response::mStatus is never initialised. - llcoproceduremanager: three cases test only boost::fibers channels, two of them with no assertion. - refcounted: a reference leaked, and comments copied from the case before. - httprequest: a disallowed header spelled with an underscore, so it can never match; a "Range" case that sends no Range header; the 503 case never reaches the peer's Retry-After list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ose libraries keep for the process TM19's domain checks, as OBJECT libraries for every binary that links the library, beside TM18's for llxml and llcharacter: - llmessage: gMessageSystem, which a case that makes a system of its own must put back, or every later case sends through it; and whether the LLProxy singleton exists, which a case that sets curl up ends with. - llcorehttp: the HTTP service's instance and state, the process's one service and worker thread, which a case that makes them must destroy before the next case's createService; and whether the websocket manager exists, whose servers each run a thread. All five TM19 binaries pass 10 random orders and 20 repeats with no report. Each check, with a case made to leak what it watches, names that case: a message system scope never ended, an LLProxy made and kept, a service created and never destroyed, a websocket manager left alive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 59 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
4 of 9 tasks
RyeMutt
added this pull request to stack #457
October 12, 2026 20:39
4 of 9 tasks
5 of 9 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
The second wave of stage 3 of the test framework's move from TUT to Catch2 (TM19): llmessage's and llcorehttp's tests. 31 TUT files with 361 tests become five Catch2 binaries. Each test file still runs in a process of its own. It follows #449 and #450, now on develop, and builds on #449's harness and CMake changes.
llmessage_testsllpipeutil.cpp) are an OBJECT library beside themllcoproceduremanager_testlltrustedmessageservice_testlltemplatemessagedispatcher_testllcorehttp_teststest_llcorehttp_peer.py, as beforeTwo production fixes first, found by stage 1:
LLMessageSystemandstart_net.~LLMessageSystemclosed its socket only when the system was OK. The template loads before the socket opens, so a system whose template failed kept an open socket and leaked it.end_netcloses only an open socket.start_netnow closes its socket on its two bind failures.LLMessageConfig::initClasswith another directory.LLLiveFile, which watches the one path it was made with. A secondinitClassmoved where it read from but not what it watched.staticvariables the header defined, one copy per file that included it, move into the.cpp, or go where nothing used them.Then the conversion:
1c6a7d9, b26bb20,
tut_parity.py:set_test_nameprinted no name, and that is listed rather than a mismatch.625dfae, llcorehttp's ten suites become test files of their own, still under TUT. They were ten
.hppfiles included into one translation unit, and they now give exactly the same 88 results.httprequest_test.cpp, their only user.using namespace LLCorecame before them.c334c14,
tut2catch.py --names: a list names each#iftwin of a test apart, here alnetimpairment'stest<1>.5c7aa17,
test/sync.h: it fails a timed-out wait the way its framework does.al_add_test_binarydefinesAL_TEST_CATCH2for every Catch2 binary. llcommon's TUT tests use the same header until TM20.ccb051c,
al_add_test_binary(... LAUNCHER <command>...):CROSSCOMPILING_EMULATOR, whichCatch.cmakeuses for discovery and each case. I checked that per-case llcorehttp cases run through the peer.5751fed, the conversion: tut2catch.py's output, with the names the idiom pass chose given through
--names. No file carries a hand edit.0c58e8e, the idiom pass:
#N);CAPTUREs replace hand-built strings;require_*;No case and no assertion was added, removed or moved.
ccf8e19, this wave's guard checks:
gMessageSystem, and whetherLLProxyexists;I broke a test for each on purpose, and each check named the case responsible.
Related Issues
Issue Link: none. This follows #449 (TM18).
Checklist
Additional Notes
ctest -LE manualpass;tut_parity.pyholds all 31 files to the TUT executables' recorded results (355 ok, 6 skipped). It holds exactly at the conversion commit and under--recountedafter the idiom pass.AL_NET_IMPAIRMENTisn't compiled here, and five HttpRequest tests skip before naming themselves.gMessageSystemback.httpstatusassignsstatus1twice where it meantstatus2.🤖 Generated with Claude Code