Revision history for Test-Log-Abstraction

0.002.1	Tue Oct 27 02:56:03 PM EDT 2026
	[ Enhancements ]
	- The level names and severities now come from Readonly::Values::Syslog,
		instead of a copy of the same table
	- Added reopen(), which does nothing and returns the logger, as
		Log::Abstraction 0.41 added it; before, a call was stored as a
		message at level reopen and printed a "no method" notice
	- more tests
	[ Bug Fixes ]
	- Bump the mininum version of Log::Abstraction, fixes GitHub#2
	- A number too big for an integer conversion in a message template, such as
		%{count}d of 1e30, is shown as written instead of as -1; this also
		fixes t/function.t on perls built with -Duselongdouble, fixes GitHub#1
		Fixes GitHub#1
	- The "no method" notice escapes control characters in the method name and
		cuts a long one, as errors already did
	- An object whose overloaded "" dies, given as a diag level name, to
		level() or as an i18n() key, no longer crashes with its own error:
		new() croaks with "invalid diag level", level() warns and returns
		undef, and i18n() returns the plain form, Class=HASH(0x...)
	- i18n() no longer dies when such an object fills a number placeholder,
		such as %{count}d: it is written in plain form, as the POD promises
		("It never fails, for any key or arguments")
	- new() reports an invalid level option as it was given ('LOUD'), not
		lower-cased, as level() and every other error do
	- An integer placeholder just outside the 64-bit range, such as %{v}d of
		-9223372036854775809 or %{v}u of 18446744073709551616, is shown as
		written instead of being rounded onto the limit
	- Values repeated in errors and warnings also escape the C1 controls
		(U+0080 to U+009F, which include the one-character CSI U+009B) when
		they are character strings; byte strings keep those bytes, which are
		usually part of UTF-8 text
	- i18n() took quadratic time on a number placeholder value with many
		leading zeros that is not all digits, such as '000...0.5' (2.3s for
		20,000 zeros): the integer-fit check now reads the zeros atomically,
		and t/regex.t proves the old and new forms agree and the new is
		linear
	- Logging no longer dies when the trailing fields hash is tied and dies
		when it is read: the hash is kept in the message, in plain form
	- _diag_rule() and _object() drop checks that earlier steps already
		prove (a defined() before comparing with 'all' and 'none'; a
		defined() and ref() before UNIVERSAL::isa()); t/logic.t proves each
		premise the removal relies on

0.002.0	Sat Oct 10 09:04:27 PM EDT 2026
	Level methods called with no arguments now match Log::Abstraction:
		warn() and more serious levels store nothing; trace() to notice()
		store an empty message
	The minimum Perl is now 5.26, which Params::Validate::Strict needs; CI
		now tests 5.26 and 5.30 as well
	Added t/transition.t: every edge of the POD state diagram, and the
		transitions it does not allow
	Added t/transaction.t: the lifecycle end to end, and new, cloning, log
		calls and assertions broken part way are all or nothing
	Security: invalid level, diag and method names repeated in errors and
		warnings are now escaped and cut to 200 characters, as validator
		errors already were; a newline in one could forge a TAP line when
		STDERR is merged into the TAP stream
	Tests: a broken error-length cap no longer prints megabytes of failure
		output (it looked like a hang under mutation testing)
	Regex audit: the placeholder pattern no longer backtracks over leading
		zeros (about 240 times faster on hostile templates); the location
		patterns in error text are linear by construction rather than by
		luck of the optimiser; the dense placeholder and lang patterns are
		commented.  Added t/regex.t
	Performance: like() and has_level() stop at the first match (about 48
		times faster when it is near the start); unlike() builds its list of
		matches only to explain a failure; plain values are rendered without
		an eval; the level table is a locked hash instead of a tied Readonly
		one.  A log call is about 20% faster.  Added t/performance.t
	_template() searched the English tables twice for an English logger;
		it now searches once (found by path analysis).  Added t/path.t: one
		test per path through every routine
	t/logic.t: complete truth tables for every compound condition (checked
		both directly and through De Morgan's law), the logger invariant
		checked before, during and after each operation, and contradictory
		inputs shown to be refused before anything changes
	Simplified the decisions in new(), _args(), _diag_rule(), _diags(),
		count(), level() and AUTOLOAD: each test is made once, cheap tests
		first, with no unreachable checks; behaviour is unchanged.  Added
		t/logic.t, one proof per outcome of each decision
	POD: the rule for which messages are printed is stated exactly
	Fixed (found by t/domain.t): "invalid argument" explanations could
		name a line inside this module; lang => 'AUTO' was rejected though
		'auto' is case-insensitive; empty() did not validate its test name
	POD: each method's API SPECIFICATION lists its valid and invalid input
		domains and their boundaries
	Fixed: a malformed UTF-8 message warned on Perl 5.36 and earlier
	Tests: run child processes without a forking pipe open, use file names
		Windows allows, and only test timeouts where they can fire, so the
		suite runs on Windows
	Fixed (found by t/data-flow.t): new() kept the caller's diag list and
		i18n tables by reference, so changing them later changed the logger
		and broke its clones; an undefined output handle warned.  Removed
		dead stores and a redundant assignment in _object, level,
		_resolve_lang, _plural and _format
	Fixed (found by t/edge_cases.t): output that failed (ENOSPC, EIO) leaked
		$! into the caller; an empty method name warned and was stored as
		'undef'; an undefined PerlIO layer warned; "invalid argument" errors
		showed the validator's internal file and line and repeated hostile
		values in full - they are now one line of at most 200 characters
	Added t/integration.t: end-to-end workflows, including parity with the
		real Log::Abstraction, permission drops and upstream failures
	POD: shows how to pass Object::Configure settings to new()
	Fixed (found by t/unit.t): like(), unlike(), has_level(), count() and
		new() cleared the caller's $@; flush() did not check its invocant
	POD: documents that $@, $!, $_ and alarm() are left alone, and lists
		every "must be called on an object" error
	Fixed (found by t/function.t): a self-referential i18n template looped
		forever; an object whose "" dies, or a tied hash that dies, made a
		log call die; logging data nested over 100 deep warned; methods
		called on undef, a plain reference or another class's object died
		with Perl's own error; a bad pattern string gave Perl's regex error;
		clones shared their fields with the original; $/ changed how the
		trailing newline was removed; %c and %d mis-rendered non-numbers
		and infinity; French plural rules were wrong for negative counts;
		several hostile inputs warned instead of being handled
	diag => 'ALL' and 'None' are accepted, as level names are
	count() validates its argument
	Fixed: French and German notices were printed as Latin-1, not UTF-8
	Documentation rewritten in plain English, with COMMON PITFALLS,
		ENCODING, FORMAL SPECIFICATION and STATE DIAGRAM sections
	Fixed: a clone ignored a new diag option; an option called 'messages'
		replaced the capture; a self-referential structure recursed forever;
		Test::Log::Abstraction::new(%opts) took the first option as the class;
		class-method calls died with "Can't use string as a HASH ref"
	Fixed: logging no longer changes $@ or $!, and fields are copied at log
		time, as in Log::Abstraction
	Added level(), is_<level>(), flush() and lang(); level methods return
		the logger; messages() returns a copy
	Added failure diagnostics listing the captured messages
	Added translatable messages (en, de, fr, zh) with lang, country and
		i18n options, and %config defaults
	Arguments are validated with Params::Validate::Strict; internals are
		private (Sub::Private) or protected (Sub::Protected)
	A lone array reference is a list of message parts and a trailing
		newline is removed, as in Log::Abstraction

0.001.0
	First draft: test double for Log::Abstraction that captures messages
	so tests can assert on them, replacing per-dist t/lib/MyLogger.pm
