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 1172fcd4dd132ef65057c64589904ea280941fdb Author: Peter Kovacs <[email protected]> AuthorDate: Sun Aug 2 00:41:10 2026 +0200 test: wire sal/strings, sw, sfx2, desktop suites; fix System32 icuuc shadowing Four more suites, and the loader landmine that blocked two of them. //main/sal:qa_rtl_strings 5 TUs -> one exe (NOT qa/rtl_strings, which is testshl dead weight; this is qa/rtl/strings) //main/sw:sw_qa_bigpointerarray 26 cases, BigPtrArray behind SwNodes //main/sfx2:sfx2_qa_metadatable Metadatable / XmlIdRegistry //main/desktop:desktop_qa_dp_version extension version ordering sw and sfx2 both died at load with STATUS_ENTRYPOINT_NOT_FOUND (0xC0000139) and an EMPTY test log. Cause: C:\Windows\System32\icuuc.dll (a ~36 KB Microsoft stub) SHADOWS the ICU 49.1.2 icuuc.dll (~1.3 MB) that AOO bundles, and putting program/ on PATH does not help — the Windows loader searches System32 BEFORE the working directory and PATH. Anything importing icuuc transitively (sw.dll, sfx.dll, …) therefore binds the system one, whose exports do not match. soffice.exe never hits this because it lives in program/ beside our copy, and the exe's OWN directory is the one location searched ahead of System32. Fix: when uno_install is set, gtest_test co-locates the ICU DLLs with the test exe, mirroring what makes soffice.exe work. Diffing all 248 staged DLL basenames against System32 shows icuuc.dll is the ONLY collision; the other two ICU DLLs are staged with it to keep the loaded ICU coherent. Empty test.log + a load-time exit code now has two known causes, worth telling apart: 0xC0000139 = a DLL loaded but an export was missing (wrong DLL won the search); 0xC0000142 = DllMain failed (the CRT activation-context / R6034 case). uno_install is also documented now as doing double duty: besides the UNO bootstrap it puts the whole office DLL closure on PATH, which is far better than enumerating dozens of transitive DLLs in runtime_dlls for anything linking a library the size of sw.dll or sfx.dll. Per the project's stated position, the resulting full-install dependency is not a reason to skip wiring a test — a slow test beats no test. Test layer: 61 -> 66 targets, 55 passing. The 11 failures are the 10 already documented in main/test/readme.md plus qa_rtl_strings' Convert.convertToString, which is the same text-conversion drift as rtl_textcvt / rtl_OUString2. No regressions from the ICU change. Co-Authored-By: Claude Opus 5 <[email protected]> --- CLAUDE.md | 20 +++++++++++++++++ build/rules/gtest_test.bzl | 55 +++++++++++++++++++++++++++++++++++++--------- main/desktop/BUILD.bazel | 34 ++++++++++++++++++++++++++++ main/sal/BUILD.bazel | 16 ++++++++++++++ main/sfx2/BUILD.bazel | 40 ++++++++++++++++++++++++++++++++- main/sw/BUILD.bazel | 34 ++++++++++++++++++++++++++++ main/test/readme.md | 29 ++++++++++++++++++++++-- 7 files changed, 215 insertions(+), 13 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 56abcffa12..a913554291 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -83,6 +83,26 @@ test 🔨 C++ unit-test infra runnable — NOW THE FRONT-LINE TASK: br LANDMINE for any new launcher: bazel test's CWD is NEITHER the exe dir NOR the execroot — locate everything from %~dp0 (see _windows_relpath in gtest_test.bzl). + LANDMINE (icu): C:\Windows\System32\icuuc.dll SHADOWS our bundled + ICU 49 icuuc.dll, and PATH cannot beat it — System32 is searched + before CWD/PATH. Anything importing it (sw.dll, sfx.dll…) dies at + load with STATUS_ENTRYPOINT_NOT_FOUND 0xC0000139 + an EMPTY + test.log; soffice.exe is immune only because it sits in program/ + next to our copy. gtest_test co-locates the ICU DLLs with the test + exe when uno_install is set. icuuc is the ONLY collision among the + 248 staged DLLs. Empty-log triage: 0xC0000139 = wrong DLL won the + search; 0xC0000142 = DllMain failed (CRT activation ctx). + Wired since: sal qa_rtl_strings, sw_qa_bigpointerarray, + sfx2_qa_metadatable, desktop_qa_dp_version. uno_install doubles as + "give me the whole office DLL closure on PATH", which beats + enumerating dozens of transitive DLLs for sw/sfx-sized libraries. + STILL TODO in this sweep: configmgr/qa/unit (builds its OWN mini + installation — urebootstrap.ini + data.xcd + generated inis — so it + needs a genrule, not //main/staging:install), cppuhelper/qa/ + propertysetmixin (UNO component DLL + own types.idl + services.rdb), + xmlsecurity/qa/certext + sal child-process suites (osl/process, + rtl/bootstrap, rtl/process) which need OfficeConnection / companion + staging, and writerfilter doctok. testtools ⬜ (bridgetest — pure-C++ UNO bridge round-trip; cli/pyuno/java variants need rules_java — see Java bucket) qadevOOo 🔨 OOoRunner.jar built (//main/qadevOOo:OOoRunner — qadevOOo QA diff --git a/build/rules/gtest_test.bzl b/build/rules/gtest_test.bzl index 2f3b6eacf6..1424e1b272 100644 --- a/build/rules/gtest_test.bzl +++ b/build/rules/gtest_test.bzl @@ -62,17 +62,26 @@ def _windows_relpath(from_dir, to_dir): def _staged_gtest_test_impl(ctx): d = ctx.label.name + ".run" staged = [] + seen = {} + + def stage(f, name = None): + name = name or f.basename + if name in seen: + return + seen[name] = True + o = ctx.actions.declare_file(d + "/" + name) + ctx.actions.symlink(output = o, target_file = f) + staged.append(o) # The gtest exe, renamed to the test target's name. staged_exe = ctx.actions.declare_file(d + "/" + ctx.label.name + ".exe") ctx.actions.symlink(output = staged_exe, target_file = ctx.executable.binary) + seen[staged_exe.basename] = True staged.append(staged_exe) # Runtime DLLs + VC90 CRT (msvcr90/msvcp90/msvcm90 + Microsoft.VC90.CRT.manifest). for f in ctx.files.runtime: - o = ctx.actions.declare_file(d + "/" + f.basename) - ctx.actions.symlink(output = o, target_file = f) - staged.append(o) + stage(f) # Companion helper exes (e.g. a child process the test spawns by name). # Staged under their own basename + a matching <name>.exe.manifest so they @@ -105,8 +114,8 @@ def _staged_gtest_test_impl(ctx): # The install root is a fixed bazel-out path (tree_install declares its # outputs in //main/staging), so locating program/fundamental.ini among the # install files at analysis time yields the execroot-relative program dir. - # `bazel test` runs with the working directory set to the execroot, hence - # %CD% below. + # The launcher then reaches it from its OWN location (%~dp0) — see + # _windows_relpath; never from %CD%, which is not the execroot. uno_program_dir = None for f in ctx.files.uno_install: if f.path.endswith("/program/fundamental.ini"): @@ -115,6 +124,26 @@ def _staged_gtest_test_impl(ctx): if ctx.files.uno_install and uno_program_dir == None: fail("uno_install does not contain program/fundamental.ini — is it //main/staging:install?") + # System32 SHADOWS one of our staged DLLs, and putting program/ on PATH does + # NOT beat it: the Windows loader searches System32 *before* the working + # directory and PATH. Windows ships its own C:\Windows\System32\icuuc.dll (a + # ~36 KB stub) while AOO bundles ICU 49.1.2 (~1.3 MB); everything importing + # icuuc — sw.dll, sfx.dll, … — then binds the system one, whose exports do + # not match, and the process dies at load with STATUS_ENTRYPOINT_NOT_FOUND + # (0xC0000139) and an EMPTY test log. soffice.exe is immune only because it + # lives in program/ next to our copy, and the exe's own directory IS searched + # first. So mirror that: co-locate ICU with the test exe. + # icuuc.dll is the only one of the 248 staged DLLs that collides; the other + # two come along to keep the loaded ICU coherent. + if uno_program_dir: + for f in ctx.files.uno_install: + if f.dirname == uno_program_dir and f.basename in [ + "icuuc.dll", + "icui18n.dll", + "icudata.dll", + ]: + stage(f) + executable = staged_exe if ctx.attr.run_in_staged_dir or uno_program_dir: launcher_dir = staged_exe.dirname # the .bat sits beside the staged exe @@ -197,11 +226,17 @@ def gtest_test( directory set to that staged dir, which co-location alone does not give you (see run_in_staged_dir in the staging rule). - uno_install: for *subsequent* tests that call - cppu::defaultBootstrap_InitialComponentContext(). Pass - //main/staging:install; the test then runs against that staged office via - URE_BOOTSTRAP. Note this makes the test depend on the whole install, so it - is far from a unit test — keep it off targets that don't need UNO. + uno_install: run the test INSIDE the staged office install (pass + //main/staging:install). Two independent things come from this, and either + one alone is a good enough reason to use it: + * a UNO bootstrap — URE_BOOTSTRAP is exported, so a test that calls + cppu::defaultBootstrap_InitialComponentContext() gets a real component + context (the *subsequent* test case); + * the whole office DLL closure on PATH — which beats enumerating dozens of + transitive DLLs in runtime_dlls for anything linking a big library like + sfx.dll or sw.dll. + It does make the test depend on the entire install, but a slow test beats no + test; build time is not a reason to skip wiring something. """ cc_binary( name = name + "_bin", diff --git a/main/desktop/BUILD.bazel b/main/desktop/BUILD.bazel index 1f96cab28d..a28069f59b 100644 --- a/main/desktop/BUILD.bazel +++ b/main/desktop/BUILD.bazel @@ -2,6 +2,7 @@ package(default_visibility = ["//visibility:public"]) load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") load("//build/rules:rsc_pipeline.bzl", "rsc_res") +load("//build/rules:gtest_test.bzl", "gtest_test") # ── Common compile settings ─────────────────────────────────────────────────── _DEFINES = [ @@ -1152,3 +1153,36 @@ exports_files(["util/soffice.exe.manifest", "util/soffice.bin.manifest"]) # this variant. Same basename so it still stages as soffice.exe/bin.manifest. exports_files(["util/amd64/soffice.exe.manifest"]) + +# ── qa unit test ────────────────────────────────────────────────── +# dp_misc::compareVersions — extension version ordering (qa/deployment_misc). +# Ports qa/deployment_misc/makefile.mk (gtest + deploymentmisc + sal). +# DISABLE_PCH_HACK comes from that makefile's CFLAGSCXX. +gtest_test( + name = "desktop_qa_dp_version", + srcs = [ + "qa/deployment_misc/test_dp_version.cxx", + "qa/deployment_misc/main.cxx", + ], + copts = _COPTS, + defines = _DEFINES + ["DISABLE_PCH_HACK"], + deps = [ + ":desktop_source_inc_hdrs", + "//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", + ":deploymentmisc_implib", + ], + linkopts = [ + "$(execpath //main/sal:sal_implib)", + "$(execpath :deploymentmisc_implib)", + ], + uno_install = "//main/staging:install", +) diff --git a/main/sal/BUILD.bazel b/main/sal/BUILD.bazel index bd001a851f..ee152cc1f3 100644 --- a/main/sal/BUILD.bazel +++ b/main/sal/BUILD.bazel @@ -327,6 +327,21 @@ sal_qa_test( srcs = ["qa/testHelperFunctions/testHelperFunctions.cxx", "qa/testHelperFunctions/testHelperFunctions2.cxx"], subdir = "testHelperFunctions", ) +# qa/rtl/strings — one exe from 5 TUs (test_oustring_noadditional.cxx supplies +# main()). NOT the same thing as qa/rtl_strings, which is testshl-based dead +# weight superseded by qa/rtl/{ostring,oustring}; this one is plain GoogleTest. +sal_qa_test( + name = "qa_rtl_strings", + srcs = [ + "qa/rtl/strings/test_oustringbuffer_utf32.cxx", + "qa/rtl/strings/test_oustring_compare.cxx", + "qa/rtl/strings/test_oustring_convert.cxx", + "qa/rtl/strings/test_oustring_endswith.cxx", + "qa/rtl/strings/test_oustring_noadditional.cxx", + ], + subdir = "rtl/strings", +) + sal_qa_test(name = "sal_ut_types", srcs = ["qa/sal/test_types.cxx"], subdir = "sal") sal_qa_test(name = "sal_ut_bytesequence", srcs = ["qa/ByteSequence/ByteSequence.cxx"], subdir = "ByteSequence") @@ -384,6 +399,7 @@ test_suite( ":tcwf", ":osl_old_test_file", ":osl_Thread", + ":qa_rtl_strings", ":osl_SocketOld", ":osl_Socket_tests", ":osl_StreamSocket", diff --git a/main/sfx2/BUILD.bazel b/main/sfx2/BUILD.bazel index 43df0687f3..ce4791aa23 100644 --- a/main/sfx2/BUILD.bazel +++ b/main/sfx2/BUILD.bazel @@ -3,6 +3,7 @@ package(default_visibility = ["//visibility:public"]) load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") load("//build/rules:rsc_pipeline.bzl", "rsc_res") load("//build/rules:sdi_target.bzl", "sdi_package", "sdi_target") +load("//build/rules:gtest_test.bzl", "gtest_test") _DEFINES = [ "WNT", "GUI", "WIN32", @@ -226,4 +227,41 @@ rsc_res( visibility = ["//visibility:public"], ) -exports_files(glob(["**/*.component"])) \ No newline at end of file +exports_files(glob(["**/*.component"])) +# ── qa unit test ────────────────────────────────────────────────── +# sfx2::Metadatable / XmlIdRegistry — RDF metadata identity and the +# copy/undo bookkeeping around it (qa/gtest/test_metadatable.cxx). +# Ports GoogleTest_sfx2_metadatable.mk (cppu cppuhelper sal sfx stl). +gtest_test( + name = "sfx2_qa_metadatable", + srcs = ["qa/gtest/test_metadatable.cxx"], + copts = _COPTS, + defines = _DEFINES, + deps = [ + ":sfx2_headers", + "//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", + ":sfx_implib", + ], + linkopts = [ + "$(execpath //main/sal:sal_implib)", + "$(execpath //main/cppu:cppu3_implib)", + "$(execpath //main/cppuhelper:cppuhelper_implib)", + "$(execpath :sfx_implib)", + ], + # Not a UNO-bootstrapping test, but sfx.dll's transitive DLL closure is + # enormous (svl, svtools, tools, vcl, comphelper, ucbhelper, …). Running + # inside the staged install supplies all of it via PATH instead of making us + # enumerate it in runtime_dlls. + uno_install = "//main/staging:install", +) diff --git a/main/sw/BUILD.bazel b/main/sw/BUILD.bazel index cb8d873536..bf76cb8804 100644 --- a/main/sw/BUILD.bazel +++ b/main/sw/BUILD.bazel @@ -3,6 +3,7 @@ package(default_visibility = ["//visibility:public"]) load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") load("//build/rules:rsc_pipeline.bzl", "rsc_res") load("//build/rules:sdi_target.bzl", "sdi_target") +load("//build/rules:gtest_test.bzl", "gtest_test") _DEFINES = [ "WNT", "GUI", "WIN32", @@ -1510,3 +1511,36 @@ filegroup( exports_files(glob(["**/*.component"])) + +# ── qa unit test ────────────────────────────────────────────────── +# BigPtrArray — the block-pointer array behind SwNodes (qa/core). +# Ports GoogleTest_sw_bigpointerarray.mk (sal sfx stl sw tl). +gtest_test( + name = "sw_qa_bigpointerarray", + srcs = ["qa/core/Test-BigPtrArray.cxx"], + copts = _COPTS + ["/Imain/sw/qa/core"], + defines = _DEFINES, + deps = [ + ":sw_headers", + "//main/sal:sal_headers", + "//main/tools:tools_headers", + "//main/sfx2:sfx2_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/tools:tl_implib", + ":sw_implib", + ], + linkopts = [ + "$(execpath //main/sal:sal_implib)", + "$(execpath //main/tools:tl_implib)", + "$(execpath :sw_implib)", + ], + # sw.dll drags in most of the office; run inside the staged install rather + # than enumerating the closure (see gtest_test's uno_install). + uno_install = "//main/staging:install", +) diff --git a/main/test/readme.md b/main/test/readme.md index b7aa4773d1..6ca14e2658 100644 --- a/main/test/readme.md +++ b/main/test/readme.md @@ -51,6 +51,10 @@ holdouts). This brings the test layer onto Bazel so suites run under - `//main/binaryurp:binaryurp_tests` — `:binaryurp_qa_cache`, `:binaryurp_qa_unmarshal` - `//main/shell:shell_qa_zip` (3) — zipfile reader over a real `.odt` + - `//main/sal:qa_rtl_strings` (5 TUs, one exe) + - `//main/sw:sw_qa_bigpointerarray` (26) — `BigPtrArray` behind `SwNodes` + - `//main/sfx2:sfx2_qa_metadatable` — `Metadatable` / `XmlIdRegistry` + - `//main/desktop:desktop_qa_dp_version` — extension version ordering - `//main/sal:osl_Socket_tests`, `:osl_StreamSocket`, `:osl_DatagramSocket`, `:osl_AcceptorSocket` (4 of the 8 socket suites; see the socket note below) @@ -143,6 +147,26 @@ holdouts). This brings the test layer onto Bazel so suites run under (Not yet handled: under `--compilation_mode=dbg` the exes link `/MDd` but this `.res` still carries the *release* CRT manifest.) +- **System32 shadows our bundled `icuuc.dll`, and `PATH` cannot beat it.** The + Windows loader searches `System32` *before* the working directory and `PATH`. + Windows ships a ~36 KB `C:\Windows\System32\icuuc.dll`; AOO bundles ICU 49.1.2 + (~1.3 MB) under the same name. Any test exe importing it transitively — + anything linking `sw.dll`, `sfx.dll`, … — therefore binds the *system* one, + whose exports don't match, and dies at load with **`STATUS_ENTRYPOINT_NOT_FOUND` + (`0xC0000139`)** and an **empty test log**. `soffice.exe` never hits this + because it lives in `program/` next to our copy, and the exe's OWN directory is + the one thing searched ahead of `System32`. + + `gtest_test` handles it: when `uno_install` is set it co-locates the ICU DLLs + with the test exe. `icuuc.dll` is the only collision among the 248 staged DLLs + (verified by diffing basenames against `System32`); the other two ICU DLLs are + staged with it to keep the loaded ICU coherent. + + Triage note: this and the R6034 bridge landmine both present as *empty + test.log + a load-time exit code*. `0xC0000139` = a DLL loaded but an export + was missing (wrong DLL won the search); `0xC0000142` = `DllMain` failed (the + CRT activation-context case). + - **Never assume the test's working directory.** It is *not* the exe's directory, and it is *not* the execroot either — a launcher that built paths from `%CD%` produced "The system cannot find the path specified" even though @@ -179,11 +203,11 @@ holdouts). This brings the test layer onto Bazel so suites run under ## The sal suite is deliberately NOT a green gate -`//main/sal:sal_tests` runs **every** migrated self-contained sal/qa test — 44 +`//main/sal:sal_tests` runs **every** migrated self-contained sal/qa test — 45 targets, passing and failing alike, on the principle that failures are information, not something to hide (the rationale lives next to the `test_suite` in [main/sal/BUILD.bazel](../sal/BUILD.bazel)). So expect it to be -red. As of 2026-08-01, 34 pass and these 10 fail, each on its **own merits** — +red. As of 2026-08-02, 34 pass and these 11 fail, each on its **own merits** — none is a build or loader problem, and the source is out of scope: | Target | Failing | Why | @@ -194,6 +218,7 @@ none is a build or loader problem, and the source is out of scope: | `rtl_logfile` | 1 | Writes/reads `c:/temp` and asserts on it — env/permission bound | | `osl_Thread` | 1 | `resume_001` is a timing race; flaky, not deterministic | | `rtl_OUString2` | 1 | `convertFromString` expects `\x80` to fail UTF-8 validation — test-data drift, same class as `rtl_textcvt` | +| `qa_rtl_strings` | 1 | `Convert.convertToString` — same text-conversion drift | | `osl_SocketOld` | 10 | see socket note below | | `osl_SocketAddr` | 3 | see socket note below | | `osl_Socket2` | 7 | see socket note below |
