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 ae2638cd9e6462aaeabd5ca6a261b6ea4c91ed2a
Author: Peter Kovacs <[email protected]>
AuthorDate: Sat Aug 1 17:26:06 2026 +0200

    test(svl): first subsequent test — in-process UNO bootstrap via 
URE_BOOTSTRAP
    
    //main/svl:svl_qa_test_URIHelper (ports GoogleTest_svl_urihelper.mk) is the
    first test on Bazel that needs a real UNO installation rather than a set of
    co-located DLLs: its fixture calls
    cppu::defaultBootstrap_InitialComponentContext().
    
    The frontier carried "needs the OfficeConnection / running-soffice fixture" 
as a
    single blocker.  It is TWO, and conflating them made the work look bigger 
than
    it is:
    
      (a) in-process bootstrap — the test builds its own component context, NO
          soffice process is involved.  This commit.
      (b) test::OfficeConnection — launches a real soffice and resolves a 
context
          over URP.  Still unwired; test.dll is built and its args already come 
from
          rtl::Bootstrap (arg-soffice=path:…, arg-user=…), so what is missing 
is the
          process lifecycle, not the plumbing.
    
    (a) is now a `uno_install` attribute on gtest_test, pointed at
    //main/staging:install.  The launcher exports ONE variable —
    URE_BOOTSTRAP=vnd.sun.star.pathname:<staged>/program/fundamental.ini — 
rather
    than reproducing the dmake recipe's hand-set UNO_TYPES / UNO_SERVICES /
    URE_INTERNAL_LIB_DIR (cppuhelper/qa/propertysetmixin/makefile.mk).
    fundamental.ini resolves ${ORIGIN} against its OWN directory, so pointing 
at it
    supplies all of them transitively, and they cannot drift when the ini 
changes.
    vnd.sun.star.pathname: takes a native path after the scheme, not a file URL
    (sal's resolvePathnameUrl → getFileURLFromSystemPath).  The launcher also 
cd's
    into program/ and prepends it to PATH so the component DLLs named in
    services.rdb load.
    
    Caveat recorded at the use site and in the rule docs: such a test depends 
on the
    ENTIRE staged install, so it is slow to build and is not a unit test in any
    meaningful sense.  Only reach for uno_install where UNO is genuinely
    bootstrapped.  This particular test is also a thin proof — its heavier case
    testNormalizedMakeRelative, the one that drives the UCB, is compiled out
    upstream behind `#define RUN_OLD_FAILING_TESTS 0`, so only
    testFindFirstURLInText runs.
    
    Landmine found on the way, now documented for any future launcher: `bazel 
test`
    runs tests with a working directory that is NEITHER the exe's directory NOR 
the
    execroot.  The first launcher built its paths from %CD% and failed with "The
    system cannot find the path specified" despite those paths being correct
    relative to the execroot.  Launchers must locate everything from %~dp0 via a
    relative path computed at analysis time (_windows_relpath), which is also 
what
    makes uno_install work from any package depth.
    
    Whole test layer re-run: 18/18 green, no regressions from the launcher 
change.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
---
 CLAUDE.md                  | 23 ++++++++---
 build/rules/gtest_test.bzl | 97 +++++++++++++++++++++++++++++++++++++---------
 main/svl/BUILD.bazel       | 53 +++++++++++++++++++++++++
 main/test/readme.md        | 51 ++++++++++++++++++++----
 4 files changed, 194 insertions(+), 30 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index 1e11722886..56abcffa12 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -65,11 +65,24 @@ test          🔨  C++ unit-test infra runnable — NOW THE 
FRONT-LINE TASK: br
                    genuinely left is not standalone — svl/qa/complex + 
svtools/qa/unoapi
                    + sfx2 + writerfilter/qa/complex are Java/UNO; 
svl/qa/test_URIHelper,
                    configmgr/qa/unit and cppuhelper/qa/propertysetmixin 
bootstrap a UNO
-                   component context.  So the front line is now ONE fixture:
-                   OfficeConnection (running-soffice).  test.dll is built and 
its
-                   arg plumbing is rtl::Bootstrap 
"arg-soffice=path:…"/"arg-user=…",
-                   so the missing piece is a rule that points it at 
//main/staging:install
-                   and gives the test exe its own UNO bootstrap.
+                   component context.  KEY DISTINCTION (was conflated under
+                   "OfficeConnection", making the work look bigger than it 
is): those
+                   are TWO fixtures.  (a) IN-PROCESS bootstrap —
+                   defaultBootstrap_InitialComponentContext(), NO soffice 
process —
+                   is DONE: gtest_test's uno_install=//main/staging:install 
exports
+                   URE_BOOTSTRAP at the staged program/fundamental.ini, whose 
${ORIGIN}
+                   then supplies UNO_TYPES/UNO_SERVICES/URE_INTERNAL_LIB_DIR
+                   transitively (no hand-set env vars, no drift).  First green:
+                   //main/svl:svl_qa_test_URIHelper.  Caveat — it depends on 
the WHOLE
+                   install, so it is slow and not a unit test; only use 
uno_install
+                   where UNO is genuinely bootstrapped.  (b) 
test::OfficeConnection
+                   (launch soffice -accept=…;urp, resolve over URP) is STILL 
UNWIRED
+                   and is the remaining front line — test.dll is built and its 
args
+                   come from rtl::Bootstrap (arg-soffice=path:…, arg-user=…), 
so what
+                   is missing is the process lifecycle, not the plumbing.
+                   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).
 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 9217f857be..2f3b6eacf6 100644
--- a/build/rules/gtest_test.bzl
+++ b/build/rules/gtest_test.bzl
@@ -42,6 +42,23 @@ _APP_MANIFEST = "//main/external/msvcp90:vc90_app_manifest"
 # msvcr90 loose → R6034 → DllMain fails → exit 0xC0000142.
 _APP_MANIFEST_RES = "//main/external/msvcp90:vc90_app_manifest_res"
 
+def _windows_relpath(from_dir, to_dir):
+    """Backslash relative path from one execroot-relative dir to another.
+
+    Used to make the launcher .bat self-locating via %~dp0 instead of trusting
+    the working directory: `bazel test` does NOT run tests with the working
+    directory set to the execroot, so a %CD%-relative path to the staged 
install
+    resolves to nothing ("The system cannot find the path specified").
+    """
+    f = from_dir.split("/")
+    t = to_dir.split("/")
+    common = 0
+    for i in range(min(len(f), len(t))):
+        if f[i] != t[i]:
+            break
+        common = i + 1
+    return "..\\" * (len(f) - common) + "\\".join(t[common:])
+
 def _staged_gtest_test_impl(ctx):
     d = ctx.label.name + ".run"
     staged = []
@@ -75,32 +92,67 @@ def _staged_gtest_test_impl(ctx):
     ctx.actions.symlink(output = man, target_file = ctx.file.app_manifest)
     staged.append(man)
 
-    # Co-locating a data file with the exe is not enough for a test that opens 
it
-    # by bare relative name: `bazel test` runs the executable with the working
-    # directory set to the execroot, not to the exe's directory (the loader 
finds
-    # the staged DLLs via the exe's own path, which is why those work 
regardless).
-    # When run_in_staged_dir is set, hand Bazel a .bat that cd's into the 
staged
-    # dir first and forwards the exit code, so relative paths resolve there.
+    # ── 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
+    # in services.rdb.  Rather than reinvent that, point it at the staged 
office
+    # via URE_BOOTSTRAP, the documented override for "which fundamental.ini
+    # describes this installation".  fundamental.ini resolves ${ORIGIN} against
+    # its OWN directory, so that one variable transitively supplies UNO_TYPES,
+    # UNO_SERVICES, URE_INTERNAL_LIB_DIR and BRAND_BASE_DIR — no need to
+    # duplicate any of them here, and no drift when the ini changes.
+    #
+    # 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.
+    uno_program_dir = None
+    for f in ctx.files.uno_install:
+        if f.path.endswith("/program/fundamental.ini"):
+            uno_program_dir = f.dirname
+            break
+    if ctx.files.uno_install and uno_program_dir == None:
+        fail("uno_install does not contain program/fundamental.ini — is it 
//main/staging:install?")
+
     executable = staged_exe
-    if ctx.attr.run_in_staged_dir:
-        launcher = ctx.actions.declare_file(d + "/" + ctx.label.name + 
"_run.bat")
-        ctx.actions.write(
-            output = launcher,
-            content = "\r\n".join([
-                "@echo off",
+    if ctx.attr.run_in_staged_dir or uno_program_dir:
+        launcher_dir = staged_exe.dirname  # the .bat sits beside the staged 
exe
+        lines = ["@echo off", "setlocal"]
+        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"',
+                # 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.
+                'set "PATH=%_PROG%;%PATH%"',
+                'cd /d "%_PROG%" || exit /b 1',
+                '"%_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 + '" %*',
-                "exit /b %ERRORLEVEL%",
-                "",
-            ]),
-            is_executable = True,
-        )
+            ]
+        lines += ["exit /b %ERRORLEVEL%", ""]
+
+        launcher = ctx.actions.declare_file(d + "/" + ctx.label.name + 
"_run.bat")
+        ctx.actions.write(output = launcher, content = "\r\n".join(lines), 
is_executable = True)
         staged.append(launcher)
         executable = launcher
 
     return [DefaultInfo(
         executable = executable,
-        runfiles = ctx.runfiles(files = staged),
+        runfiles = ctx.runfiles(files = staged + ctx.files.uno_install),
         files = depset([executable]),
     )]
 
@@ -113,6 +165,7 @@ _staged_gtest_test = rule(
         "companions": attr.label_list(cfg = "target"),
         "app_manifest": attr.label(allow_single_file = True, default = 
_APP_MANIFEST),
         "run_in_staged_dir": attr.bool(default = False),
+        "uno_install": attr.label(allow_files = True),
     },
 )
 
@@ -131,6 +184,7 @@ def gtest_test(
         defines = [],
         runtime_dlls = [],
         data_files = [],
+        uno_install = None,
         companions = [],
         additional_linker_inputs = [],
         linkopts = [],
@@ -142,6 +196,12 @@ def gtest_test(
     They are staged beside the exe AND the test is launched with its working
     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.
     """
     cc_binary(
         name = name + "_bin",
@@ -169,5 +229,6 @@ def gtest_test(
         runtime = runtime_dlls + data_files + [_CRT],
         companions = companions,
         run_in_staged_dir = bool(data_files),
+        uno_install = uno_install,
         size = size,
     )
diff --git a/main/svl/BUILD.bazel b/main/svl/BUILD.bazel
index e6c39278f1..92ef5712a2 100644
--- a/main/svl/BUILD.bazel
+++ b/main/svl/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")
 
 _DEFINES = [
     "WNT", "GUI", "WIN32",
@@ -208,5 +209,57 @@ rsc_res(
     visibility = ["//visibility:public"],
 )
 
+# ── qa unit test ──────────────────────────────────────────────────
+# URIHelper::FindFirstURLInText (qa/test_URIHelper.cxx).  Ports
+# GoogleTest_svl_urihelper.mk (cppu cppuhelper sal svl stl tl utl).
+#
+# This is the first *subsequent* test on Bazel: its fixture calls
+# cppu::defaultBootstrap_InitialComponentContext(), so it needs a real UNO
+# installation rather than just a set of co-located DLLs.  uno_install points 
it
+# at the staged office and the gtest_test launcher exports URE_BOOTSTRAP — see
+# //build/rules:gtest_test.bzl.  That makes this test depend on the ENTIRE
+# install, so it is slow to build and is not a unit test in any real sense.
+#
+# It does NOT need a running soffice (that is test::OfficeConnection, still
+# unwired): the component context is bootstrapped in-process.  Note the file's
+# heavier case, testNormalizedMakeRelative — the one that drives the UCB — is
+# compiled out upstream behind `#define RUN_OLD_FAILING_TESTS 0`, so only
+# testFindFirstURLInText actually runs.
+gtest_test(
+    name = "svl_qa_test_URIHelper",
+    srcs = ["qa/test_URIHelper.cxx"],
+    copts = _COPTS + ["/Imain/svl/inc/svl"],
+    defines = _DEFINES,
+    deps = [
+        ":svl_headers",
+        "//main/sal:sal_headers",
+        "//main/cppu:cppu_headers",
+        "//main/cppuhelper:cppuhelper_headers",
+        "//main/tools:tools_headers",
+        "//main/unotools:unotools_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",
+        "//main/tools:tl_implib",
+        "//main/unotools:utl_implib",
+        ":svl_implib",
+    ],
+    linkopts = [
+        "$(execpath //main/sal:sal_implib)",
+        "$(execpath //main/cppu:cppu3_implib)",
+        "$(execpath //main/cppuhelper:cppuhelper_implib)",
+        "$(execpath //main/tools:tl_implib)",
+        "$(execpath //main/unotools:utl_implib)",
+        "$(execpath :svl_implib)",
+    ],
+    uno_install = "//main/staging:install",
+)
+
 exports_files(glob(["**/*.component"]))
 
diff --git a/main/test/readme.md b/main/test/readme.md
index 7157875f45..b7aa4773d1 100644
--- a/main/test/readme.md
+++ b/main/test/readme.md
@@ -70,8 +70,38 @@ holdouts).  This brings the test layer onto Bazel so suites 
run under
    import lib of every DLL whose symbols the test TU references directly
    (e.g. `cppu3_implib` for `uno_any_destruct`/`typelib_*` pulled in by an
    `Any` destructor or `getCppuType`).
-2. **Subsequent / UNO tests** — use `test::OfficeConnection` to launch a real
-   soffice. Need the staged install wired as a fixture. **Not yet wired.**
+2. **Subsequent / UNO tests** — need a real UNO installation, not just
+   co-located DLLs. These are **two different fixtures**, long conflated under
+   the one name "OfficeConnection":
+
+   a. **In-process bootstrap** — the test calls
+      `cppu::defaultBootstrap_InitialComponentContext()` and builds its own
+      component context. **No soffice process is involved.** WIRED: pass
+      `uno_install = "//main/staging:install"` to `gtest_test`.
+
+      The launcher exports a single **`URE_BOOTSTRAP`** pointing at the staged
+      `program/fundamental.ini`. That is the whole trick: the dmake recipe
+      (`cppuhelper/qa/propertysetmixin/makefile.mk`) sets `UNO_TYPES`,
+      `UNO_SERVICES`, `URE_INTERNAL_LIB_DIR`, … by hand, but `fundamental.ini`
+      resolves `${ORIGIN}` against *its own* directory, so pointing at it
+      supplies all of them transitively — and they cannot drift when the ini
+      changes. `URE_BOOTSTRAP` takes a `vnd.sun.star.pathname:` URL (a *native*
+      path after the scheme; `sal`'s `resolvePathnameUrl` converts it).
+      The launcher also `cd`s into `program/` and prepends it to `PATH` so the
+      component DLLs named in `services.rdb` load.
+
+      Green: `//main/svl:svl_qa_test_URIHelper`.
+
+      **Cost:** the test depends on the entire staged install, so it is slow to
+      build and is not a unit test in any meaningful sense. Do not reach for
+      `uno_install` unless the test genuinely bootstraps UNO.
+
+   b. **Running-office connection** — `test::OfficeConnection` (libtest) starts
+      a real soffice with `-accept=pipe,name=…;urp` and resolves a remote
+      context over URP. **Still unwired.** `test.dll` is built and its 
arguments
+      come from `rtl::Bootstrap` (`arg-soffice=path:<soffice.exe>`,
+      `arg-user=<user installation>`), so what is missing is the process
+      lifecycle, not the plumbing.
 
 ## Gotchas (learned the hard way)
 
@@ -113,11 +143,18 @@ 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.)
 
-- **Staging a data file beside the exe is not enough — the working directory is
-  the execroot.** `bazel test` launches the test with its CWD set to the
-  execroot, *not* the exe's directory. Co-located DLLs still resolve (the 
loader
-  searches the exe's own path), which makes it easy to assume relative file
-  opens will too — they don't. A test that opens a fixture by bare relative 
name
+- **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
+  those paths were correct relative to the execroot. Anything the launcher 
needs
+  to find must be located from **`%~dp0`** (the .bat's own directory) via a
+  relative path computed at analysis time — `_windows_relpath` in
+  [gtest_test.bzl](../../build/rules/gtest_test.bzl) does this, and it is why
+  `uno_install` works from any package depth.
+
+- **Staging a data file beside the exe is not enough.** Co-located DLLs resolve
+  regardless (the loader searches the exe's own path), which makes it easy to
+  assume relative file opens will too — they don't. A test that opens a 
fixture by bare relative name
   (`//main/shell:shell_qa_zip` → `simpledocument.odt`) throws
   file-not-found while passing when run by hand from the staged dir. Pass such
   inputs as `data_files`: they are staged beside the exe *and* the target

Reply via email to