This is an automated email from the ASF dual-hosted git repository. leginee pushed a commit to branch bazel-migration in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit fc3b72efc8eeab318efe05634af22b9823599d4e Author: Peter Kovacs <[email protected]> AuthorDate: Wed Aug 5 07:28:32 2026 +0200 test(configmgr): fixture (c) — own mini installation; qa/unit wired RED Four new gtest_test capabilities, all general, and the finding that the test which drove them cannot be made green from the build system. FIXTURE (c) — "own mini installation": the test supplies its own registry data and builds its own service manager, with no default bootstrap and no soffice. Distinct from (a) in-process bootstrap and (b) OfficeConnection. * data_tree={label:"rel/path"} — staging at a CHOSEN path rather than flat by basename. Needed when the SHAPE of the tree is the fixture: $ORIGIN resolves against an ini's own directory, and a mini installation has two files named bootstrap.ini. * ure_bootstrap="rel/path" — point URE_BOOTSTRAP at a staged ini instead of the install's program/fundamental.ini. Still requires uno_install: only the DATA root moves, the UNO DLL closure stays on PATH from program/. * env={} / prerun=[] — extra environment, and launcher command lines run before the exe (each || exit /b 1, so a fixture that fails to build fails the test loudly). Both expand $(RUNDIR)/$(PROGRAM)/$(SCRATCH)/ $(SCRATCH_URL). prerun exists for fixtures only buildable AT RUN TIME: a registry a UNO tool must write would, as a build action, need its own staged DLL closure and CRT manifest, and the component location it records is an ABSOLUTE path that has no business in a cacheable artifact. $(SCRATCH) is a fresh, writable, per-run dir under TEST_TMPDIR, wiped before AND after. Everything a test writes belongs there — the staged dir is bazel-out, i.e. build output. office_connection's user installation is now this same mechanism (test_qa_officeconnection still 2/2, ~13s). BACKSLASH RULE, RESTATED BECAUSE IT CUTS BOTH WAYS: an env value the test reads back through rtl::Bootstrap::get() is macro-expanded, so its backslashes must be DOUBLED (arg-user); one read with plain getenv() must NOT be (CONFIGMGR_UNIT_FORWARD_STRING). Prefer a file:/// URL where the consumer takes one — forward slashes sidestep the question. configmgr/qa/unit is WIRED AND RED, and cannot go green without a source change. It builds and links, then dies in prerun with "configmgr.uno.dll: cannot get symbol: component_writeInfo". The blocker recorded on the frontier for this test — "needs a custom ure_bootstrap root + extra env" — was read off qa/unit/makefile.mk and was WRONG. That fixture is built here and works; the mini installation stages and resolves. The real wall is that the test's bootstrap PREMISE was retired under it: regcomp -register registers a component by calling component_writeInfo(), the pre-.component mechanism, and AOO dropped it. configmgr/source/services.cxx exports only component_getFactory and component_getImplementationEnvironment; tree-wide there are 438 .component files against a handful of component_writeInfo exporters, all odk examples, workbench samples and legacy tests. The obvious escape is closed too: test.cxx calls the ONE-ARGUMENT createRegistryServiceFactory overload, which defaults bReadOnly to sal_False, so SimpleRegistry::open() receives (readonly=false, create=true) — and the textual (XML) registry branch is reachable only when bReadOnly && !bCreate. UNO_SERVICES does not help: it belongs to defaultBootstrap_InitialComponentContext, not that deprecated path. Upstream knows. The commit that moved this suite to GoogleTest (7231f715d2, "#i125003#") says in its own message: "All tests fail and on Windows it doesn't start running, but these look like problems from before and at least it doesn't run during the build." dmake gates every qa/ dir behind ENABLE_UNIT_TESTS=NO, so nobody has run it since — the same bit-rot as the missing <windows.h> in sal's osl/process suites, just fatal instead of cosmetic. Kept wired because it BUILDS, per the policy that a buildable test gets wired even when red on its own merits (cf. writerfilter_qa_doctok, red on a fixture never checked in). The regcomp line is deliberately left in place: its error names the retired mechanism exactly, which beats a vaguer failure later inside Test::SetUp. Fixing it means a source change — port test.cxx to defaultBootstrap_InitialComponentContext + a services.rdb (i.e. make it fixture (a), already supported), pass sal_True for bReadOnly with a textual registry, or re-export component_writeInfo (reviving what AOO retired). Also corrects the frontier's "3/3" for test_qa_officeconnection: it is 2 cases. Co-Authored-By: Claude Opus 5 <[email protected]> --- CLAUDE.md | 63 +++++++++++++-- build/rules/gtest_test.bzl | 188 +++++++++++++++++++++++++++++++++++++++----- main/configmgr/BUILD.bazel | 189 +++++++++++++++++++++++++++++++++++++++++++++ main/configmgr/readme.md | 120 ++++++++++++++++++++++++++++ main/test/readme.md | 67 +++++++++++++++- 5 files changed, 601 insertions(+), 26 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index ce1d4525b7..938985834d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -84,7 +84,7 @@ test 🔨 C++ unit-test infra runnable — NOW THE FRONT-LINE TASK: br (launch soffice -accept=…;urp, resolve over URP) is DONE 2026-08-05: gtest_test office_connection=True (+ uno_install) makes a throwaway user installation and exports arg-soffice/arg-user. GREEN: - //main/test:test_qa_officeconnection (3/3 runs, ~15s — real office + //main/test:test_qa_officeconnection (2/2, ~13s — real office boot → URP resolve → remote Desktop → clean terminate). NOTHING TO PORT from dmake: solenv's C++ APP1TEST rule (_tg_app.mk) runs the bare exe with only --gtest_output and never sets these args @@ -135,15 +135,64 @@ test 🔨 C++ unit-test infra runnable — NOW THE FRONT-LINE TASK: br enumerating dozens of transitive DLLs for sw/sfx-sized libraries. writerfilter_qa_doctok wired too (RED on its own missing fixture: reads <cwd>/test.doc, never checked in; testInitUno passes). + FIXTURE (c) DONE 2026-08-05 — "own mini installation": the test + supplies its own registry data and builds its own service manager, + no default bootstrap and no soffice. Four NEW general gtest_test + attrs, all reusable: data_tree={label:"rel/path"} (staging at a + CHOSEN path, not flat by basename — $ORIGIN resolves against an + ini's own dir and a mini installation has TWO files named + bootstrap.ini), ure_bootstrap="rel/path" (point URE_BOOTSTRAP at a + staged ini instead of the install's program/fundamental.ini; STILL + needs uno_install — only the DATA root moves, the DLL closure stays + on PATH from program/), env={} and prerun=[] (extra env vars, and + launcher command lines run before the exe, each || exit /b 1). Both + expand $(RUNDIR)/$(PROGRAM)/$(SCRATCH)/$(SCRATCH_URL). $(SCRATCH) is + a fresh WRITABLE dir under TEST_TMPDIR wiped before AND after — + everything a test writes goes there, since the staged dir is + bazel-out and must be read-only; office_connection's user + installation is now this same mechanism. prerun exists for fixtures + only buildable AT RUN TIME (a registry a UNO tool must write: as a + build action it needs its own DLL closure + CRT manifest, and the + location it records is an ABSOLUTE path that must not enter a cached + artifact). BACKSLASH RULE CUTS BOTH WAYS: an env value read back via + rtl::Bootstrap::get() is macro-expanded so must be DOUBLED + (arg-user); one read with plain getenv() must NOT be + (CONFIGMGR_UNIT_FORWARD_STRING). Prefer a file:/// URL where the + consumer takes one. STILL TODO, with what each actually needs: - • configmgr/qa/unit — its OWN mini installation (urebootstrap.ini + - data.xcd + 2 generated inis) plus unit.rdb = types.rdb with - configmgr.uno registered via //main/cpputools:regcomp (that tool - IS built), and env CONFIGMGR_UNIT_FORWARD_STRING=<unit.rdb>. - Needs gtest_test to accept a CUSTOM ure_bootstrap root + extra - env, not just uno_install=//main/staging:install. + • configmgr/qa/unit — WIRED 2026-08-05 but RED, and NOT fixable + without a SOURCE change. The blocker recorded here before ("needs + a custom ure_bootstrap root + extra env") was read off + qa/unit/makefile.mk and was WRONG: that fixture is built and works + (mini installation stages + resolves). The real wall is that the + test's BOOTSTRAP PREMISE was retired under it — regcomp -register + registers via component_writeInfo(), the pre-.component mechanism, + and configmgr/source/services.cxx exports only + component_getFactory + component_getImplementationEnvironment + (tree-wide: 438 .component files vs a handful of + component_writeInfo exporters, all odk examples/workbench/legacy + tests) ⇒ "cannot get symbol: component_writeInfo". The escape of + feeding it a TEXTUAL (XML) services registry is closed: test.cxx + calls the 1-ARG createRegistryServiceFactory overload, which + defaults bReadOnly=sal_False, so SimpleRegistry::open() gets + (readonly=false, create=true) and the textual branch needs + bReadOnly && !bCreate. UNO_SERVICES belongs to + defaultBootstrap_InitialComponentContext, not this path. UPSTREAM + KNOWS: the GoogleTest-migration commit 7231f715d2 (#i125003#) says + "All tests fail and on Windows it doesn't start running". Kept + wired (it BUILDS) with the regcomp line deliberately left in — its + error names the retired mechanism exactly. Fix = port test.cxx to + defaultBootstrap_InitialComponentContext + services.rdb (i.e. + fixture (a), already supported), or pass bReadOnly=sal_True with a + textual registry, or re-export component_writeInfo (worst). + GENERAL LESSON: a qa/ dir gated behind ENABLE_UNIT_TESTS=NO for a + decade may encode a mechanism the product no longer has — check + the test's BOOTSTRAP PATH against current source before costing + the fixture. • cppuhelper/qa/propertysetmixin — UNO component DLL + own types.idl + a per-test services.rdb (packcomponents.xslt). + Uses the MODERN .component mechanism, so it should not hit the + configmgr wall; fixture (c)'s data_tree/env/prerun should cover it. • xmlsecurity/qa/certext — BLOCKED, and NOT on fixture (b) as recorded here before: it #includes <neon/ne_ssl.h> and calls ne_ssl_cert_read(), but AOO deleted neon in favour of curl. Would diff --git a/build/rules/gtest_test.bzl b/build/rules/gtest_test.bzl index 9b100ec977..2ed6785146 100644 --- a/build/rules/gtest_test.bzl +++ b/build/rules/gtest_test.bzl @@ -59,6 +59,47 @@ def _windows_relpath(from_dir, to_dir): common = i + 1 return "..\\" * (len(f) - common) + "\\".join(t[common:]) +# Tokens usable in `env` values and `prerun` command lines. They stand for +# paths only knowable at run time, and expand to launcher variables: +# $(RUNDIR) the staged directory (native, no trailing separator) +# $(PROGRAM) the staged install's program/ dir (needs uno_install) +# $(SCRATCH) a fresh, empty, WRITABLE per-run directory (native) +# $(SCRATCH_URL) the same directory as forward slashes, for a file:/// URL +# Everything a test writes belongs under $(SCRATCH): the staged dir lives in +# bazel-out and must be treated as read-only build output. +_TOKENS = [ + # $(SCRATCH_URL) FIRST — otherwise $(SCRATCH) matches its prefix. + ("$(SCRATCH_URL)", "%_SCRATCHU%"), + ("$(SCRATCH)", "%_SCRATCH%"), + ("$(RUNDIR)", "%_RUN%"), + ("$(PROGRAM)", "%_PROG%"), +] + +def _expand_tokens(s): + for token, var in _TOKENS: + s = s.replace(token, var) + return s + +def _uses_token(strings, token): + for s in strings: + if s.find(token) != -1: + return True + return False + +def _env_lines(env): + # sorted() so the launcher is byte-identical across analyses (a dict's + # iteration order is stable in Starlark, but the attr comes from a BUILD file + # where the author's ordering carries no meaning). + return ['set "%s=%s"' % (k, _expand_tokens(env[k])) for k in sorted(env)] + +def _prerun_lines(prerun): + # `|| exit /b 1` on every one: a fixture that failed to build must fail the + # test loudly, not leave the suite to report a confusing downstream error. + out = [] + for cmd in prerun: + out.append(_expand_tokens(cmd) + " || exit /b 1") + return out + def _staged_gtest_test_impl(ctx): # The staging dir is normally "<name>.run". bin_layout makes it # "<name>.run/bin" instead — the ONE thing the child-process suites need. @@ -110,6 +151,20 @@ def _staged_gtest_test_impl(ctx): ctx.actions.symlink(output = man, target_file = ctx.file.app_manifest) staged.append(man) + # data_tree — fixture files staged at an EXPLICIT relative path rather than + # flat by basename (which is all `runtime` can express). Needed whenever the + # shape of the tree is what is under test: a mini UNO installation resolves + # $ORIGIN against the ini's own directory, so basis/program/uno.ini has to be + # exactly there, and two files in it are both named "bootstrap.ini". + for label, rel in ctx.attr.data_tree.items(): + files = label[DefaultInfo].files.to_list() + if len(files) != 1: + fail("data_tree entry %s must provide exactly one file, got %d" % + (label.label, len(files))) + o = ctx.actions.declare_file(d + "/" + rel) + ctx.actions.symlink(output = o, target_file = files[0]) + staged.append(o) + # ── UNO environment (subsequent / in-process-bootstrap tests) ──────────── # A test that calls cppu::defaultBootstrap_InitialComponentContext() needs a # real UNO installation: type + service rdbs, and every component DLL named @@ -158,18 +213,82 @@ def _staged_gtest_test_impl(ctx): "bootstraps an in-process context to build the URL resolver, and " + "the soffice it launches comes from the staged install.") + # A launcher .bat is needed for anything the bare exe cannot express itself: + # a working directory, an environment, or a fixture built at run time. + env = ctx.attr.env + prerun = ctx.attr.prerun + values = env.values() + prerun + need_scratch = (ctx.attr.office_connection or + _uses_token(values, "$(SCRATCH")) + if _uses_token(values, "$(PROGRAM)") and not uno_program_dir: + fail("$(PROGRAM) needs uno_install — there is no staged install to point at.") + if ctx.attr.ure_bootstrap and not uno_program_dir: + fail("ure_bootstrap needs uno_install: it only REDIRECTS the bootstrap " + + "ini, it does not supply the UNO DLL closure the test still loads.") + executable = staged_exe - if ctx.attr.run_in_staged_dir or uno_program_dir: + if (ctx.attr.run_in_staged_dir or uno_program_dir or env or prerun): launcher_dir = staged_exe.dirname # the .bat sits beside the staged exe lines = ["@echo off", "setlocal"] + + # %~dp0 keeps its trailing backslash, which does not concatenate cleanly + # and cannot be quoted next to one; "%~dp0." normalized by %%~fI gives the + # same directory without it. + lines += ['for %%I in ("%~dp0.") do set "_RUN=%%~fI"'] + + if need_scratch: + # Everything the test writes goes here. Wiped BEFORE as well as + # after: a run that dies without cleaning up must not hand its state + # to the next one (a stale user installation is exactly the kind of + # thing that makes a failure unreproducible). + lines += [ + 'set "_SCRATCH=%TEST_TMPDIR%\\scratch"', + 'if "%TEST_TMPDIR%"=="" set "_SCRATCH=%_RUN%\\scratch"', + # TEST_TMPDIR arrives with forward slashes; the native form is + # what osl's getFileURLFromSystemPath and cmd both want. + 'set "_SCRATCH=%_SCRATCH:/=\\%"', + 'if exist "%_SCRATCH%" rmdir /s /q "%_SCRATCH%"', + 'mkdir "%_SCRATCH%" || exit /b 1', + # file:/// URL form: only the separators differ on Windows. + 'set "_SCRATCHU=%_SCRATCH:\\=/%"', + ] + if uno_program_dir: lines += [ 'set "_EXE=%~dp0' + staged_exe.basename + '"', # Resolved from the launcher's own location (%~dp0), not %CD%. 'for %%I in ("%~dp0' + _windows_relpath(launcher_dir, uno_program_dir) + '") do set "_PROG=%%~fI"', - # vnd.sun.star.pathname: takes a native path, not a file URL. - 'set "URE_BOOTSTRAP=vnd.sun.star.pathname:%_PROG%\\fundamental.ini"', + ] + + # Which installation the UNO bootstrap describes. Normally the + # staged office (program/fundamental.ini, whose ${ORIGIN} then + # supplies UNO_TYPES / UNO_SERVICES / URE_INTERNAL_LIB_DIR). A test + # that is ABOUT the configuration layer needs its own tiny + # installation instead, so it can assert on data it controls rather + # than on whatever the real registry happens to hold — that is what + # ure_bootstrap redirects to. The DLLs still come from program/ via + # PATH below; only the data root moves. + # + # NOTE single backslashes here, unlike arg-soffice/arg-user further + # down. URE_BOOTSTRAP is consumed by the bootstrap machinery itself + # (rtl_bootstrap_args_open on the ini path) and is NOT put through + # macro expansion, so its separators survive. Values that a test + # later reads back via rtl::Bootstrap::get() ARE expanded, and those + # must double their backslashes. + # + # vnd.sun.star.pathname: takes a native path, not a file URL. + if ctx.attr.ure_bootstrap: + lines += [ + 'set "URE_BOOTSTRAP=vnd.sun.star.pathname:%_RUN%\\' + + ctx.attr.ure_bootstrap.replace("/", "\\") + '"', + ] + else: + lines += [ + 'set "URE_BOOTSTRAP=vnd.sun.star.pathname:%_PROG%\\fundamental.ini"', + ] + + lines += [ # Component DLLs named in services.rdb are loaded at run time and # live in program/; the exe's own directory still wins for what it # imports directly, so its staged copies are unaffected. @@ -213,14 +332,10 @@ def _staged_gtest_test_impl(ctx): # //build/testsupport:sal_process_init (no WSAStartup for the # socket suites); the environment route needs no shim TU and # cannot be broken by a future suite's choice of main(). + # The user installation is just the general scratch directory — + # created and wiped by the block above, which is why there is no + # per-run bookkeeping here. lines += [ - 'set "_USER=%TEST_TMPDIR%\\oootest_user"', - 'if "%TEST_TMPDIR%"=="" set "_USER=%~dp0oootest_user"', - # TEST_TMPDIR arrives with forward slashes; osl's - # getFileURLFromSystemPath wants native separators. - 'set "_USER=%_USER:/=\\%"', - 'if exist "%_USER%" rmdir /s /q "%_USER%"', - 'mkdir "%_USER%" || exit /b 1', 'set "_SOFFICE=%_PROG%\\soffice.exe"', # A BACKSLASH IS AN ESCAPE CHARACTER in a bootstrap value: # rtl::Bootstrap runs every value it returns through macro @@ -233,23 +348,25 @@ def _staged_gtest_test_impl(ctx): # hit this: it passes a file:// URL, which has no # backslashes.) 'set "arg-soffice=path:%_SOFFICE:\\=\\\\%"', - 'set "arg-user=%_USER:\\=\\\\%"', + 'set "arg-user=%_SCRATCH:\\=\\\\%"', ] - lines += ['"%_EXE%" %*'] + lines += _env_lines(env) + _prerun_lines(prerun) + ['"%_EXE%" %*'] else: # Co-locating a data file with the exe is not enough for a test that # opens it by bare relative name: the working directory is the # execroot, not the exe's directory (the loader finds the staged DLLs # via the exe's own path, which is why those work regardless). - lines += [ - 'cd /d "%~dp0" || exit /b 1', - '"%~dp0' + staged_exe.basename + '" %*', - ] + # Only when that was actually asked for — a launcher that exists + # solely to set `env` must not silently move the cwd as well. + if ctx.attr.run_in_staged_dir: + lines += ['cd /d "%~dp0" || exit /b 1'] + lines += _env_lines(env) + _prerun_lines(prerun) + lines += ['"%~dp0' + staged_exe.basename + '" %*'] # Capture the exit code BEFORE any cleanup — rmdir would clobber it. lines += ['set "_RC=%ERRORLEVEL%"'] - if ctx.attr.office_connection: - lines += ['rmdir /s /q "%_USER%" 2>nul'] + if need_scratch: + lines += ['rmdir /s /q "%_SCRATCH%" 2>nul'] lines += ["exit /b %_RC%", ""] launcher = ctx.actions.declare_file(d + "/" + ctx.label.name + "_run.bat") @@ -275,6 +392,10 @@ _staged_gtest_test = rule( "bin_layout": attr.bool(default = False), "office_connection": attr.bool(default = False), "uno_install": attr.label(allow_files = True), + "data_tree": attr.label_keyed_string_dict(allow_files = True), + "env": attr.string_dict(), + "prerun": attr.string_list(), + "ure_bootstrap": attr.string(), }, ) @@ -297,6 +418,10 @@ def gtest_test( companions = [], bin_layout = False, office_connection = False, + data_tree = {}, + env = {}, + prerun = [], + ure_bootstrap = None, additional_linker_inputs = [], linkopts = [], size = None, @@ -337,6 +462,29 @@ def gtest_test( failure mode: OfficeConnection::setUp() retries the resolve in an UNBOUNDED loop, so if the office never comes up the test timeout is the only thing that ends it. Do not drop this to "small". + + data_tree: {label: "relative/staged/path"} — like data_files, but staged at + a path you choose instead of flat by basename. Use it when the SHAPE of the + tree is the fixture: a mini UNO installation resolves $ORIGIN against the + ini's own directory, and two of its files are both named "bootstrap.ini". + + env / prerun: extra environment variables, and command lines run in the + launcher just before the exe (each is `|| exit /b 1`-guarded). Both expand + $(RUNDIR) / $(PROGRAM) / $(SCRATCH) / $(SCRATCH_URL) — see _TOKENS. + prerun exists for fixtures that can only be BUILT at run time: a registry a + UNO tool has to write, for instance, where doing it as a build action would + mean bundling that tool with its own CRT manifest and DLL closure, and would + bake an absolute bazel-out path into a cached artifact. + NOTE an env value that the test reads back through rtl::Bootstrap::get() + goes through macro expansion, so its backslashes must be DOUBLED; one read + with plain getenv() must not be. + + ure_bootstrap: staged-relative path (a data_tree entry) to a bootstrap ini + to use as URE_BOOTSTRAP instead of the install's program/fundamental.ini — + i.e. "run against THIS installation's data". For tests of the configuration + layer itself, which need a registry they control rather than the real one. + Requires uno_install: it moves the data root only, and the UNO DLL closure + still comes from program/ via PATH. """ if size == None: size = "medium" if office_connection else "small" @@ -369,5 +517,9 @@ def gtest_test( bin_layout = bin_layout, office_connection = office_connection, uno_install = uno_install, + data_tree = data_tree, + env = env, + prerun = prerun, + ure_bootstrap = ure_bootstrap or "", size = size, ) diff --git a/main/configmgr/BUILD.bazel b/main/configmgr/BUILD.bazel index a2f120cc91..0fe5d132b8 100644 --- a/main/configmgr/BUILD.bazel +++ b/main/configmgr/BUILD.bazel @@ -1,6 +1,8 @@ package(default_visibility = ["//visibility:public"]) load("@rules_cc//cc:defs.bzl", "cc_binary") +load("@bazel_skylib//rules:write_file.bzl", "write_file") +load("//build/rules:gtest_test.bzl", "gtest_test") _DEFINES = [ "WNT", "GUI", "WIN32", @@ -87,3 +89,190 @@ cc_binary( exports_files(glob(["**/*.component"])) +# ── qa/unit — the configuration layer against its OWN mini installation ── +# +# STATUS: WIRED AND RED, and it cannot go green without a SOURCE change (out of +# scope). It builds and links; it dies in the `prerun` fixture step with +# configmgr.uno.dll: cannot get symbol: component_writeInfo +# That is not a migration defect. `regcomp -register` registers a component by +# calling component_writeInfo() on it — the PRE-.component registration +# mechanism — and AOO retired that in the 3-layer-office removal: configmgr's +# services.cxx exports only component_getFactory and +# component_getImplementationEnvironment, and the tree now has 438 .component +# files against a handful of component_writeInfo exporters (odk examples, +# workbench samples, legacy tests). qa/unit/makefile.mk was never updated. +# +# The obvious escape — hand it a textual (XML) services registry instead of a +# binary one — is closed: test.cxx calls the ONE-argument +# createRegistryServiceFactory overload, which defaults bReadOnly to sal_False, +# so SimpleRegistry::open() gets (readonly=false, create=true), and the textual +# branch is reachable only when bReadOnly && !bCreate (simpleregistry.cxx). +# UNO_SERVICES does not help either: it belongs to +# defaultBootstrap_InitialComponentContext, not to this deprecated path. +# +# Upstream knows. The commit that moved this suite to GoogleTest (7231f715d2, +# "#i125003#") says in its own message: "All tests fail and on Windows it doesn't +# start running, but these look like problems from before and at least it doesn't +# run during the build" — dmake gates every qa/ dir behind ENABLE_UNIT_TESTS=NO, +# so nobody has run it since. Same class of bit-rot as the missing <windows.h> +# in sal's osl/process suites, just fatal instead of cosmetic. +# +# Kept wired anyway, per the policy that a test which BUILDS gets wired even when +# it is red on its own merits (cf. //main/writerfilter:writerfilter_qa_doctok, +# red on a fixture that was never checked in). The regcomp line is deliberately +# left in place: its error message names the retired mechanism exactly, which is +# more useful than a vaguer failure later inside Test::SetUp. +# +# To fix it, one of these — all source changes: +# * port test.cxx to defaultBootstrap_InitialComponentContext + a services.rdb +# (i.e. make it fixture (a), which gtest_test already supports); or +# * pass sal_True for bReadOnly and feed it a textual services registry; or +# * re-export component_writeInfo from configmgr (reviving what AOO retired — +# the worst of the three). +# +# Everything BELOW this notice is verified working and is the reason the target +# stays: the mini installation stages and resolves, and the four gtest_test +# capabilities it drove (data_tree / ure_bootstrap / env / prerun) are general. +# +# This is a THIRD test fixture flavour, distinct from the two in +# build/rules/gtest_test.bzl's notes. It is not the in-process bootstrap of the +# staged office (a), nor a launched soffice over URP (b): the test builds its own +# service manager with cppu::createRegistryServiceFactory(unit.rdb) and points +# the bootstrap at a four-file installation whose entire share/registry is one +# data.xcd it ships. That is the whole point — a test that asserts on +# /org.openoffice.Setup Test/AString cannot run against the real registry, which +# has no such key, and one that WRITES keys must not touch the real user profile. +# +# Mirrors the TEST target in qa/unit/makefile.mk, whose recipe is: +# * a mini installation under $(MISC)/unitdata (below); +# * unit.rdb = a copy of types.rdb with configmgr.uno registered into it via +# regcomp, passed to the test in CONFIGMGR_UNIT_FORWARD_STRING; +# * URE_BOOTSTRAP pointed at unitdata/urebootstrap.ini. +# +# Two deliberate divergences from that recipe, both forced: +# +# 1. unit.rdb is built by the LAUNCHER (gtest_test prerun), not by a build +# action. regcomp bootstraps a UNO service manager, so as a build action it +# would need its own staged DLL closure and CRT manifest — and worse, the +# location it records for a component is an ABSOLUTE path +# (registercomponent.cxx::convertToFileUrl), which a cached artifact must not +# contain. Built per run in the scratch dir, the path it bakes in is the +# staged install that this very run is using, and nothing goes stale. +# `-br types.rdb` (which dmake omits) keeps regcomp's OWN bootstrap off the +# default registry: with it, cppuhelper builds the service manager from the +# type registry plus its hardcoded bootstrap components, so regcomp never +# consults the URE_BOOTSTRAP we have just redirected at the mini installation. +# +# 2. bootstrap.ini is written to basis/program/ as well as brand/program/. +# The makefile writes it only under brand/, but AOO's Components:: +# getModificationFileUrl() reads +# ${$OOO_BASE_DIR/program/bootstrap.ini:UserInstallation} — OOO_BASE_DIR, i.e. +# basis/ — so under the recipe as written the lookup yields nothing and every +# write test loses its changes. The fixture is stale, not the code; since it +# is generated build data rather than source, it is fixed here. +# UserInstallation also cannot be the makefile's $ORIGIN/../../user: that +# resolves inside bazel-out, which is build output and must be treated as +# read-only. It indirects through $CONFIGMGR_UNIT_USER instead, which the +# launcher points at the per-run scratch dir (writeModFile() does +# Directory::createPath, so the leaf need not exist). + +write_file( + name = "qa_unit_uno_ini", + out = "qa/unit/gen/uno.ini", + content = [ + "[Bootstrap]", + "UNO_SHARED_PACKAGES_CACHE = $OOO_BASE_DIR", + "UNO_USER_PACKAGES_CACHE = ${$OOO_BASE_DIR/program/bootstrap.ini:UserInstallation}", + "", + ], +) + +# $CONFIGMGR_UNIT_USER is expanded by rtl::Bootstrap from the environment; the +# launcher sets it to a file:/// URL under the scratch dir. A URL, not a native +# path, so there are no backslashes for bootstrap macro expansion to eat. +# +# TWO targets with identical content because data_tree is keyed by LABEL: one +# label cannot be staged to two paths, and this file is needed at both +# basis/program/ (what Components::getModificationFileUrl reads) and +# brand/program/ (what the dmake recipe writes — kept for parity, in case +# anything resolves it through BRAND_BASE_DIR). +[ + write_file( + name = "qa_unit_bootstrap_ini_" + where, + out = "qa/unit/gen/" + where + "/bootstrap.ini", + content = [ + "[Bootstrap]", + "UserInstallation = $CONFIGMGR_UNIT_USER", + "", + ], + ) + for where in [ + "basis", + "brand", + ] +] + +gtest_test( + name = "configmgr_qa_unit", + srcs = ["qa/unit/test.cxx"], + copts = [ + "/Imain/configmgr/inc/pch", + "/Zc:wchar_t-", + ], + defines = [ + "WNT", + "GUI", + "WIN32", + "snprintf=_snprintf", + ], + deps = [ + "//main/sal:sal_headers", + "//main/cppu:cppu_headers", + "//main/cppuhelper:cppuhelper_headers", + "//main/udkapi:udkapi_idl_headers", + "//main/offapi:offapi_idl_headers", + "//main/stlport:stlport", + "@boost.legacy//:boost.legacy", + ], + additional_linker_inputs = [ + "//main/sal:sal_implib", + "//main/cppu:cppu3_implib", + "//main/cppuhelper:cppuhelper_implib", + ], + linkopts = [ + "$(execpath //main/sal:sal_implib)", + "$(execpath //main/cppu:cppu3_implib)", + "$(execpath //main/cppuhelper:cppuhelper_implib)", + ], + # The mini installation. Paths are load-bearing: urebootstrap.ini resolves + # $ORIGIN against its OWN directory to reach basis/ and brand/, and the two + # bootstrap.ini files would collide under flat basename staging. + data_tree = { + "qa/unit/urebootstrap.ini": "unitdata/urebootstrap.ini", + ":qa_unit_uno_ini": "unitdata/basis/program/uno.ini", + ":qa_unit_bootstrap_ini_basis": "unitdata/basis/program/bootstrap.ini", + "qa/unit/data.xcd": "unitdata/basis/share/registry/data.xcd", + ":qa_unit_bootstrap_ini_brand": "unitdata/brand/program/bootstrap.ini", + }, + ure_bootstrap = "unitdata/urebootstrap.ini", + # regcomp is staged beside the test exe (with its own CRT manifest) and run + # by the prerun lines below. + companions = ["//main/cpputools:regcomp"], + prerun = [ + 'copy /y "$(PROGRAM)\\types.rdb" "$(SCRATCH)\\unit.rdb" >nul', + '"$(RUNDIR)\\regcomp.exe" -register -br "$(PROGRAM)\\types.rdb" ' + + '-r "$(SCRATCH)\\unit.rdb" -c "$(PROGRAM)\\configmgr.uno.dll"', + ], + env = { + # Read with getenv() (test.cxx Test::SetUp), so NOT bootstrap-expanded: + # single backslashes, unlike arg-user in the office_connection fixture. + "CONFIGMGR_UNIT_FORWARD_STRING": "$(SCRATCH)\\unit.rdb", + "CONFIGMGR_UNIT_USER": "file:///$(SCRATCH_URL)/user", + }, + # No runtime_dlls: with uno_install, co-locating a core UNO DLL breaks the + # bootstrap (cppuhelper resolves its component dir from its own module + # handle, and the exe's directory beats PATH). program/ already has the + # whole closure, including configmgr.uno.dll itself. + uno_install = "//main/staging:install", +) + diff --git a/main/configmgr/readme.md b/main/configmgr/readme.md index 4fa0a9d4ea..c5e5998bb0 100644 --- a/main/configmgr/readme.md +++ b/main/configmgr/readme.md @@ -1,6 +1,126 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> + # Notes for configmgr (done) - Single DLL: `configmgr.uno.dll` - Deps: sal, cppu, cppuhelper, salhelper, comphelper, xmlreader, udkapi, offapi, stlport, boost.legacy - Requires `/Zc:wchar_t-` and stlport (uses boost::unordered_map, hash_map) - Exports via SAL_DLLPUBLIC_EXPORT — no DEF file + +## qa/unit — `//main/configmgr:configmgr_qa_unit` + +> **Status: wired and RED. It cannot go green without a source change.** +> +> It builds and links, then dies in the `prerun` fixture step with +> `configmgr.uno.dll: cannot get symbol: component_writeInfo`. +> +> `regcomp -register` registers a component by calling `component_writeInfo()` +> on it — the **pre-`.component` registration mechanism**. AOO retired that: +> `configmgr/source/services.cxx` exports only `component_getFactory` and +> `component_getImplementationEnvironment`, and the tree now has 438 +> `.component` files against a handful of `component_writeInfo` exporters (odk +> examples, workbench samples, legacy tests). `qa/unit/makefile.mk` was never +> updated to match. +> +> The obvious escape — hand it a **textual** (XML) services registry instead of +> a binary one — is closed. `test.cxx` calls the *one-argument* +> `createRegistryServiceFactory` overload, which defaults `bReadOnly` to +> `sal_False`, so `SimpleRegistry::open()` receives `(readonly=false, +> create=true)`; the textual branch is reachable only when +> `bReadOnly && !bCreate`. `UNO_SERVICES` does not help either — that belongs to +> `defaultBootstrap_InitialComponentContext`, not to this deprecated path. +> +> **Upstream knows.** The commit that moved this suite to GoogleTest +> (`7231f715d2`, `#i125003#`) says so in its own message: *"All tests fail and on +> Windows it doesn't start running, but these look like problems from before and +> at least it doesn't run during the build."* dmake gates every `qa/` dir behind +> `ENABLE_UNIT_TESTS=NO`, so nobody has run it since. Same class of bit-rot as +> the missing `<windows.h>` in sal's `osl/process` suites — just fatal rather +> than cosmetic. +> +> It stays wired because it *builds*, per the policy that a buildable test gets +> wired even when red on its own merits (cf. +> `//main/writerfilter:writerfilter_qa_doctok`, red on a fixture never checked +> in). The `regcomp` line is deliberately left in: its error names the retired +> mechanism exactly, which beats a vaguer failure later inside `Test::SetUp`. +> +> Fixing it means one of these, all **source** changes: port `test.cxx` to +> `defaultBootstrap_InitialComponentContext` + a `services.rdb` (i.e. make it +> fixture (a), already supported); or pass `sal_True` for `bReadOnly` and feed it +> a textual services registry; or re-export `component_writeInfo` from configmgr +> (reviving what AOO retired — the worst of the three). +> +> Everything below is verified working, and is why the target stays: the mini +> installation stages and resolves, and the four `gtest_test` capabilities it +> drove (`data_tree` / `ure_bootstrap` / `env` / `prerun`) are general. + +Mirrors the `TEST` target in `qa/unit/makefile.mk`. A THIRD test fixture +flavour, next to the two described in `main/test/readme.md`: not an in-process +bootstrap of the staged office (a), and not a launched soffice over URP (b). +The test builds its own service manager with +`cppu::createRegistryServiceFactory(unit.rdb)` and runs against a four-file +installation of its own, whose entire `share/registry` is the `data.xcd` it +ships. That is the point of it — the assertions are on +`/org.openoffice.Setup Test/AString`, a key the real registry does not have, and +the write cases must not touch a real user profile. + +Staged layout (`data_tree`, so the paths are exact — `urebootstrap.ini` resolves +`$ORIGIN` against its own directory, and two files are named `bootstrap.ini`): + + unitdata/urebootstrap.ini OOO_BASE_DIR=$ORIGIN/basis, BRAND_BASE_DIR=$ORIGIN/brand + unitdata/basis/program/uno.ini generated + unitdata/basis/program/bootstrap.ini generated (UserInstallation) + unitdata/basis/share/registry/data.xcd the whole registry under test + unitdata/brand/program/bootstrap.ini generated (same content) + +`ure_bootstrap` points `URE_BOOTSTRAP` at `unitdata/urebootstrap.ini` instead of +the staged office's `program/fundamental.ini`; `uno_install` is still needed, +because only the DATA root moves — the UNO DLL closure (including +`configmgr.uno.dll` itself) still comes from `program/` via `PATH`. + +### Two divergences from the dmake recipe, both forced + +**`unit.rdb` is built by the launcher, not by a build action.** `regcomp` +bootstraps a UNO service manager, so as a build action it would need its own +staged DLL closure and CRT manifest; worse, the location it records for a +component is an ABSOLUTE path (`registercomponent.cxx::convertToFileUrl`), which +has no business inside a cacheable artifact. Built per run in the scratch dir, +the path it bakes in is the staged install this run is actually using. +`-br types.rdb` is added (dmake omits it) to keep regcomp's own bootstrap off the +default registry — with it, cppuhelper builds the service manager from the type +registry plus its hardcoded `bootstrap.uno.dll` components, so regcomp never +consults the `URE_BOOTSTRAP` we just redirected at the mini installation. + +**`bootstrap.ini` is written to `basis/program/` as well as `brand/program/`.** +The makefile writes it only under `brand/`, but AOO's +`Components::getModificationFileUrl()` reads +`${$OOO_BASE_DIR/program/bootstrap.ini:UserInstallation}` — `OOO_BASE_DIR`, i.e. +`basis/`. Under the recipe as written that lookup yields nothing and every write +test silently loses its changes; the fixture is stale, not the code. It is +generated build data rather than source, so it is fixed here rather than worked +around. `UserInstallation` also cannot be the makefile's `$ORIGIN/../../user`, +which resolves inside `bazel-out` — build output, to be treated as read-only. It +indirects through `$CONFIGMGR_UNIT_USER`, which the launcher points at the +per-run scratch dir (`writeModFile()` does `Directory::createPath`, so the leaf +need not pre-exist). + +`CONFIGMGR_UNIT_FORWARD_STRING` (the path to `unit.rdb`) is read with plain +`getenv()` in `Test::SetUp`, so unlike `arg-user` in fixture (b) it must NOT have +its backslashes doubled — nothing macro-expands it. diff --git a/main/test/readme.md b/main/test/readme.md index 1f34fd0682..fd69423c74 100644 --- a/main/test/readme.md +++ b/main/test/readme.md @@ -133,11 +133,76 @@ holdouts). This brings the test layer onto Bazel so suites run under Note `OfficeConnection::setUp()` retries the resolve in an **unbounded** loop, so a soffice that never accepts hangs until the Bazel test timeout. - That is why `office_connection` defaults `size` to `large`. + That is why `office_connection` defaults `size` to `medium` (300 s) — + `small`'s 60 s is barely 3× the observed run, and the timeout is the only + thing that ends a run where the office never comes up. Green: `//main/test:test_qa_officeconnection` (~13 s — a full office boot, URP resolve, remote service creation and clean terminate). + c. **Own mini installation** — the test supplies its own registry data and + builds its own service manager, typically with + `cppu::createRegistryServiceFactory(<rdb>)`. Neither (a) nor (b): no + default bootstrap, no soffice. This is what a test *of the configuration + layer* needs, because it must assert on keys the real registry does not + have and write keys without touching a real user profile. + + Four `gtest_test` attributes cover it, and none is specific to that one + test: + + - **`data_tree = {label: "relative/path"}`** — staging at a path you + choose rather than flat by basename. Needed when the *shape* of the tree + is the fixture: `$ORIGIN` in a bootstrap ini resolves against that ini's + own directory, and a mini installation has two files named + `bootstrap.ini`. + - **`ure_bootstrap = "relative/path"`** — point `URE_BOOTSTRAP` at one of + those staged inis instead of the install's `program/fundamental.ini`. + Still requires `uno_install`: only the DATA root moves, and the UNO DLL + closure still comes from `program/` via `PATH`. + - **`env = {…}`** and **`prerun = […]`** — extra environment variables, + and command lines run in the launcher just before the exe (each + `|| exit /b 1`-guarded, so a fixture that fails to build fails the test + loudly). Both expand `$(RUNDIR)`, `$(PROGRAM)`, `$(SCRATCH)` and + `$(SCRATCH_URL)`. + + `$(SCRATCH)` is a fresh, empty, **writable** directory under + `TEST_TMPDIR`, wiped before *and* after the run. Everything a test writes + belongs there: the staged directory lives in `bazel-out` and must be + treated as read-only build output. `office_connection`'s user installation + is just this same scratch dir. + + `prerun` exists for fixtures that can only be built **at run time**. The + configmgr case is the archetype: its registry has to be written by + `regcomp`, a tool that bootstraps UNO — as a build action that would need + its own staged DLL closure and CRT manifest, and the component location it + records is an *absolute* path, which must not end up in a cacheable + artifact. + + **Backslash rule, restated because it cuts both ways.** An `env` value the + test reads back through `rtl::Bootstrap::get()` is macro-expanded, so its + backslashes must be **doubled** (`arg-user`). One read with plain + `getenv()` must **not** be (`CONFIGMGR_UNIT_FORWARD_STRING`). Prefer a + `file:///` URL where the consumer accepts one — forward slashes sidestep + the question entirely. + + Wired: `//main/configmgr:configmgr_qa_unit` — **red, and not fixable + without a source change**. The fixture machinery above works; the test + itself bootstraps through `createRegistryServiceFactory` + `regcomp + -register`, i.e. the pre-`.component` registration mechanism that AOO + retired, so `regcomp` cannot find `component_writeInfo` in + `configmgr.uno.dll`. Upstream's own GoogleTest-migration commit + (`7231f715d2`) records that the suite already failed and "on Windows it + doesn't start running". See `main/configmgr/readme.md` for the full + diagnosis, the staged layout, and the two forced divergences from + `qa/unit/makefile.mk`. + + **Lesson for the remaining fixture work:** a `qa/` dir that dmake has + gated behind `ENABLE_UNIT_TESTS=NO` for a decade may encode a mechanism + the product no longer has. Check the test's *bootstrap path* against + current source before costing the fixture — the blocker recorded for this + one ("needs a custom `ure_bootstrap` root + extra env") was read off the + makefile and was not the real obstacle. + ## Gotchas (learned the hard way) - **The C++/UNO bridge DLL is a run-time dep that nothing links.**
