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 eb867b6ad9bb851755ff98492ada70948b5b0794 Author: Peter Kovacs <[email protected]> AuthorDate: Wed Aug 5 22:10:26 2026 +0200 test(testtools): bridgetest GREEN — C++ and Java UNO bridge round-trip Wires the UNO bridge round-trip suite: every construct the type system has (each simple type, strings, enums, structs, polymorphic structs, sequences, anys, interfaces, attributes, out/inout params, exceptions, multiple inheritance, current context, recursive and sequence-of-calls dispatch) pushed through a call chain and checked coming back. //main/testtools:bridgetest CppTestObject, ~1.1s //main/testtools:bridgetest_java JavaTestObject, ~1.4s //main/testtools:bridgetest_tests both The Java half is the first verification that the java_uno JNI bridge MARSHALS correctly — cppuhelper/qa/propertysetmixin only proved a Java UNO component can be loaded. Not a GoogleTest, and deliberately not made into one (that would be a source change): three UNO components driven by the generic //main/cpputools uno.exe, which instantiates the -s service, queries XMain and calls run() with everything after "--". run()'s return value is the exit code. The driver takes the NAME of the object to test, which is exactly why one driver serves the C++, Java, Python and CLI objects. New reusable rule attr `run_args` on staged_run_test: a fixed command line baked into the launcher and token-expanded like `env`, so the test binary can be a generic tool the suite configures. //build:jre.bzl centralizes the machine-specific, arch-select()ed test JRE, previously inline in cppuhelper. The C++ target is a fourth fixture kind: uno.exe -ro calls bootstrap_InitialComponentContext(REGISTRY), not defaultBootstrap, so there is no fundamental.ini and no uno_install. Two facts fall out that contradict notes recorded elsewhere, both verified here: * the TEXTUAL (XML) registry works on this path — openRegistry passes (bReadOnly=true, bCreate=false), the combination configmgr/qa/unit cannot get from its one-argument createRegistryServiceFactory; * a relative component uri ("./cppobj.uno.dll") is resolved by textualservices.cxx with rtl::Uri::convertRelToAbs against the rdb FILE's own URL, so it needs no bootstrap variable and no env at all. LANDMINE, second and wider mechanism of "co-locating a core UNO DLL breaks the bootstrap": cppu::get_unorc() opens get_this_libpath()+"/uno.ini", i.e. uno.ini beside cppuhelper3MSC.dll, and uno.ini — not fundamental.ini — is where URE_INTERNAL_LIB_DIR is defined. Every vnd.sun.star.expand: URI resolves through that handle, so a co-located cppuhelper3MSC.dll makes all of them silently expand to nothing. It surfaced two layers away as jvmfwk's "The file: vnd.sun.star.expand:$URE_INTERNAL_LIB_DIR/sunjavaplugin.dll does not exist". Hence the two targets' runtime lists are opposites and both are correct. x86 green; x64 analysis-only checked (the mscx_uno select resolves). Co-Authored-By: Claude Opus 5 <[email protected]> --- CLAUDE.md | 67 +++- build/jre.bzl | 46 +++ build/rules/gtest_test.bzl | 28 +- main/cppuhelper/BUILD.bazel | 40 +- main/testtools/BUILD.bazel | 420 +++++++++++++++++++++ main/testtools/readme.md | 183 +++++++++ .../testtools/source/bridgetest/bridgetest.uno.def | 11 + .../source/bridgetest/constructors.uno.def | 11 + main/testtools/source/bridgetest/cppobj.uno.def | 11 + 9 files changed, 774 insertions(+), 43 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 040e2f783f..04cb651cab 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -110,6 +110,17 @@ test 🔨 C++ unit-test infra runnable — NOW THE FRONT-LINE TASK: br component library failed: …/<test>.run/bootstrap.uno.dll"). List only test-only DLLs; the office closure is already on PATH via program/ (svl_qa_test_URIHelper lists none, which is why it passed). + SECOND, WIDER MECHANISM found 2026-08-05 via testtools/bridgetest_java + — same cause, but it breaks MACRO EXPANSION rather than one DLL load, + so the symptom can appear layers away with no mention of a DLL: + cppu::get_unorc() opens get_this_libpath()+"/uno.ini", i.e. uno.ini + BESIDE cppuhelper3MSC.dll, and uno.ini — NOT fundamental.ini — is where + URE_INTERNAL_LIB_DIR is defined. Every vnd.sun.star.expand: URI in the + tree goes through that handle (bootstrap_expandUri → + cppuhelper::detail::expandMacros), so a co-located cppuhelper3MSC.dll + makes all of them silently expand to NOTHING. It surfaced as jvmfwk's + "The file: vnd.sun.star.expand:$URE_INTERNAL_LIB_DIR/sunjavaplugin.dll + does not exist" + the catch-all "JRE could not be recognized". CONSUMER NOTE: upstream's ONLY C++ user of the fixture, xmlsecurity/qa/certext, CANNOT BUILD — it needs neon (ne_ssl_cert_read) and AOO replaced neon with curl (no main/neon, no @@ -253,8 +264,60 @@ test 🔨 C++ unit-test infra runnable — NOW THE FRONT-LINE TASK: br not build: osl_process asserts an env ORDER Windows doesn't use; rtl_Bootstrap expects the default ini to be testshl2.ini because the testshl2-era process was literally testshl2.exe. -testtools ⬜ (bridgetest — pure-C++ UNO bridge round-trip; cli/pyuno/java variants - need rules_java — see Java bucket) +testtools 🔨 bridgetest GREEN 2026-08-05, BOTH halves — //main/testtools:bridgetest + (C++ object, ~1.1s) and :bridgetest_java (Java object over the + java_uno JNI bridge, ~1.4s); :bridgetest_tests runs both. This is + the widest type-marshalling check in the tree (every simple type, + string, enum, struct, POLYMORPHIC struct, sequence, any, interface, + attribute, out/inout param, exception, multiple inheritance, current + context, recursive + sequence-of-calls dispatch) and the Java half is + the FIRST verification that the java_uno bridge marshals correctly — + propertysetmixin only proved a Java component can be LOADED. + NOT a gtest and must not become one (that would be a source change): + it is 3 UNO components driven by the generic //main/cpputools uno.exe, + which instantiates the -s service, queries XMain and calls run() with + everything after "--"; the driver takes the NAME of the object to test, + which is exactly why ONE driver serves the C++/Java/Python/CLI objects. + run() 's return value IS the exit code, so no wrapper is needed. + NEW general rule attr `run_args` (gtest_test.bzl, on staged_run_test): + a FIXED command line baked into the launcher and token-expanded like + `env`, so the "test binary" can be a generic tool the suite configures. + FOURTH FIXTURE KIND (the C++ half): uno.exe -ro calls + bootstrap_InitialComponentContext(REGISTRY), not defaultBootstrap — + no fundamental.ini, no URE_BOOTSTRAP, NO uno_install, ~1s. Everything + else is hardcoded in cppuhelper bootstrapInitialSF() out of + bootstrap.uno.dll. Two reusable facts: (1) the TEXTUAL (XML) registry + WORKS on this path — openRegistry passes (bReadOnly=true, + bCreate=false), exactly the combination configmgr/qa/unit cannot get + from its 1-arg createRegistryServiceFactory; (2) a RELATIVE component + uri ("./cppobj.uno.dll") is resolved by textualservices.cxx with + rtl::Uri::convertRelToAbs against the RDB FILE's own URL, so "beside + this rdb" needs no bootstrap variable and no env at all. + LANDMINE — the co-located-UNO-DLL rule has a SECOND, bigger mechanism + than the recorded one (bootstrap.uno.dll via get_this_libpath): + cppu::get_unorc() opens get_this_libpath()+"/uno.ini", i.e. uno.ini + BESIDE cppuhelper3MSC.dll, and uno.ini is where URE_INTERNAL_LIB_DIR + is defined (fundamental.ini does NOT define it). EVERY + vnd.sun.star.expand: URI resolves through that one handle + (bootstrap_expandUri → cppuhelper::detail::expandMacros). Co-locate + cppuhelper3MSC.dll and the exe's dir wins the loader search ⇒ lookup + lands in the test dir ⇒ no uno.ini ⇒ every such macro silently expands + to NOTHING. Surfaced two layers away as jvmfwk's "The file: + vnd.sun.star.expand:$URE_INTERNAL_LIB_DIR/sunjavaplugin.dll does not + exist" + "could not be recognized". So with uno_install, `runtime` + must list ONLY test-only files: the C++ target lists the whole core + stack and the Java one lists NONE — opposite, both correct. + Also: the Java run needs the INSTALL's services.rdb as a 3rd -ro + (dmake's $(SOLARXMLDIR)/ure/services.rdb). Registering just the two + Java2-loader components instead does NOT work — javaloader resolves + the component URL via com.sun.star.uri.UriReferenceFactory, itself a + URE component (stoc uriproc), so it only moves the failure one service + along. Passed as a file:/// URL (unoexe convertToFileUrl takes it + verbatim). //build:jre.bzl now centralizes the machine-specific, + arch-select()ed test JRE (was duplicated in cppuhelper). + See main/testtools/readme.md. STILL ⬜: cli + cliversioning + qa/cli + (cli_ure bucket), pyuno variant (reachable now), source/performance + (a benchmark), and the socket-URP client/server variants. qadevOOo 🔨 OOoRunner.jar built (//main/qadevOOo:OOoRunner — qadevOOo QA framework, ~2137 classes; classpath ridl/unoil/jurt/juh_jar/ java_uno_jar; .csv objdsc NOT jarred, manifest omitted). diff --git a/build/jre.bzl b/build/jre.bzl new file mode 100644 index 0000000000..05120073b9 --- /dev/null +++ b/build/jre.bzl @@ -0,0 +1,46 @@ +"""The JRE a *test* starts in-process, per target architecture. + +Any test that puts jvmfwk into DIRECT mode (UNO_JAVA_JFW_JREHOME) loads a +jvm.dll into its OWN process, so the JRE has to match the TARGET arch, not the +host's — a 32-bit default build cannot load the 64-bit JVM that $JAVA_HOME +almost certainly points at, and one JAVA_HOME cannot be right for both arches +anyway. Hence a select(), and hence not the build JDK: @remotejdk21_win is the +toolchain that COMPILES (and is x64-only), which is a different question from +which JVM a test may load. + +This is machine-specific and the one genuinely unhermetic input in the test +suite. It lives here rather than in each BUILD file so that pointing the tree +at a different JDK is a single edit; //main/bridges' java_run_test still takes +its own `jvm_path_dirs` (it needs the DIRECTORY containing jvm.dll for PATH, +not a home URL), so that is a related but distinct knob. + +A URL, not a path, and mind two encodings: + * forward slashes — every rtl::Bootstrap value is macro-expanded, where a + backslash is an ESCAPE character, so "C:\\Program" comes back "C:Program"; + * %20 for spaces — osl's file-URL parsing requires it. +And note the launcher doubles '%' for cmd before substituting (gtest_test.bzl +_expand_tokens); without that "%20" reaches the test as "0". +""" + +_JRE_X86 = "file:///C:/Program%20Files%20(x86)/Eclipse%20Adoptium/jdk-8.0.452.9-hotspot" +_JRE_X64 = "file:///C:/Program%20Files/Eclipse%20Adoptium/jdk-8.0.452.9-hotspot" + +def jre_home_env(): + """select() giving UNO_JAVA_JFW_JREHOME for the target arch. + + Merge it into a gtest_test/staged_run_test `env` dict: + + env = { ... } | jre_home_env() + + Setting any UNO_JAVA_JFW_{JREHOME,ENV_JREHOME,CLASSPATH,PARAMETER_1} is what + selects jvmfwk's DIRECT mode (fwkbase.cxx getMode). Its other mode, + JFW_MODE_APPLICATION, reads a javasettings_<os>_<arch>.xml recording which + JRE a human picked in Tools > Options > Java; a fixture has no such history, + so jfw_startVM() would return JFW_E_JAVA_DISABLED before looking for a JVM. + Direct mode skips only that settings layer — sunjavaplugin.dll still probes + the JRE, so vendor recognition is still exercised. + """ + return select({ + "//build:arch_x64": {"UNO_JAVA_JFW_JREHOME": _JRE_X64}, + "//conditions:default": {"UNO_JAVA_JFW_JREHOME": _JRE_X86}, + }) diff --git a/build/rules/gtest_test.bzl b/build/rules/gtest_test.bzl index bfdd0b99b8..a5837a9689 100644 --- a/build/rules/gtest_test.bzl +++ b/build/rules/gtest_test.bzl @@ -114,6 +114,12 @@ def _env_lines(env): # where the author's ordering carries no meaning). return ['set "%s=%s"' % (k, _expand_tokens(env[k])) for k in sorted(env)] +def _arg_string(run_args): + # Quoted so an argument containing a space survives cmd's tokenizing, and + # token-expanded like `env` values are — a fixed command line routinely has + # to name a staged file, which is only locatable at run time. + return "".join([' "%s"' % _expand_tokens(a) for a in run_args]) + 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. @@ -249,7 +255,9 @@ def _staged_gtest_test_impl(ctx): # a working directory, an environment, or a fixture built at run time. env = ctx.attr.env prerun = ctx.attr.prerun - values = env.values() + prerun + run_args = ctx.attr.run_args + argstr = _arg_string(run_args) + values = env.values() + prerun + run_args need_scratch = (ctx.attr.office_connection or _uses_token(values, "$(SCRATCH")) if _uses_token(values, "$(PROGRAM)") and not uno_program_dir: @@ -259,7 +267,7 @@ def _staged_gtest_test_impl(ctx): "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 or env or prerun): + if (ctx.attr.run_in_staged_dir or uno_program_dir or env or prerun or run_args): launcher_dir = staged_exe.dirname # the .bat sits beside the staged exe lines = ["@echo off", "setlocal"] @@ -387,7 +395,8 @@ def _staged_gtest_test_impl(ctx): 'set "arg-soffice=path:%_SOFFICE:\\=\\\\%"', 'set "arg-user=%_SCRATCH:\\=\\\\%"', ] - lines += _env_lines(env) + _prerun_lines(prerun) + ['"%_EXE%" %*'] + lines += _env_lines(env) + _prerun_lines(prerun) + lines += ['"%_EXE%"' + argstr + " %*"] 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 @@ -398,7 +407,7 @@ def _staged_gtest_test_impl(ctx): 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 + '" %*'] + lines += ['"%~dp0' + staged_exe.basename + '"' + argstr + " %*"] # Capture the exit code BEFORE any cleanup — rmdir would clobber it. lines += ['set "_RC=%ERRORLEVEL%"'] @@ -432,6 +441,7 @@ _staged_gtest_test = rule( "data_tree": attr.label_keyed_string_dict(allow_files = True), "env": attr.string_dict(), "prerun": attr.string_list(), + "run_args": attr.string_list(), "ure_bootstrap": attr.string(), }, ) @@ -440,7 +450,15 @@ _staged_gtest_test = rule( # runtime DLLs + VC90 CRT + external manifest into one flat dir and runs it as a # test (pass/fail = process exit code). Exposed for non-gtest runnable tests, # e.g. bridges' inter_libs_exc (a cross-DLL C++ exception smoke test whose exe -# loads two sibling DLLs and exits 0 iff exception propagation works). +# loads two sibling DLLs and exits 0 iff exception propagation works), and +# testtools' bridgetest, where the "test binary" is the generic //main/cpputools +# uno.exe and the suite is selected entirely by `run_args`. +# +# run_args: a FIXED command line baked into the launcher, token-expanded like +# `env` (so it can name a staged file). Anything passed on the `bazel test` +# command line still follows it, via %*. Deliberately not the native `args` +# attribute: baking it in keeps `bazel run` on the target reproducing exactly +# what `bazel test` ran. staged_run_test = _staged_gtest_test def gtest_test( diff --git a/main/cppuhelper/BUILD.bazel b/main/cppuhelper/BUILD.bazel index 272851d593..61d55ad0e7 100644 --- a/main/cppuhelper/BUILD.bazel +++ b/main/cppuhelper/BUILD.bazel @@ -5,6 +5,7 @@ load("@rules_java//java:defs.bzl", "java_import", "java_library") load("//build/rules:idl_pipeline.bzl", "idl_library") load("//build/rules:java_pipeline.bzl", "jar_from_directory", "javamaker_classes", "uno_jar") load("//build/rules:merge_rdb.bzl", "merge_rdb") +load("//build:jre.bzl", "jre_home_env") load("//build/rules:gtest_test.bzl", "gtest_test") load("//main/postprocess:postprocess.bzl", "services_rdb") @@ -439,41 +440,8 @@ gtest_test( # Where the vnd.sun.star.expand: URIs in psm_services.rdb resolve to. "OOO_INBUILD_SHAREDLIB_DIR": "file:///$(RUNDIR_URL)", "OOO_INBUILD_JAR_DIR": "file:///$(RUNDIR_URL)", - # Put jvmfwk in DIRECT mode with an explicit JRE. - # - # Its other mode, JFW_MODE_APPLICATION, reads a javasettings_<os>_<arch> - # .xml recording which JRE the user picked and whether Java is enabled — - # written by Tools > Options > Java, i.e. by a human, at some point in - # the past. A test fixture has no such history, so jfw_startVM() would - # return JFW_E_JAVA_DISABLED before ever looking for a JVM. Direct mode - # (any of UNO_JAVA_JFW_{JREHOME,ENV_JREHOME,CLASSPATH,PARAMETER_1} set — - # fwkbase.cxx getMode) skips the settings layer and takes what it is - # given. sunjavaplugin.dll still inspects it, so that path is exercised. - # - # The JRE must match the TARGET arch: this test process is 32-bit for - # the default build and loads a 32-bit jvm.dll into itself. That rules - # out the ENV_JREHOME/$JAVA_HOME form — one JAVA_HOME cannot be right - # for both arches, and bazel does not forward it to tests anyway. - # - # A URL, not a path, and forward slashes: UNO_JAVA_JFW_JREHOME is taken - # as-is (unlike ENV_JREHOME, which goes through - # getFileURLFromSystemPath), and every rtl::Bootstrap value is macro- - # expanded, where a backslash is an ESCAPE character — "C:\Program" - # would come back "C:Program". Spaces must be %20-encoded for osl's - # file URL parsing. - # - # TODO: machine-specific, same as //main/bridges test_any_jni's - # jvm_path_dirs. Both want one build setting for "the JRE for this - # arch". Set below, per arch. - } | select({ - "//build:arch_x64": { - "UNO_JAVA_JFW_JREHOME": - "file:///C:/Program%20Files/Eclipse%20Adoptium/jdk-8.0.452.9-hotspot", - }, - "//conditions:default": { - "UNO_JAVA_JFW_JREHOME": - "file:///C:/Program%20Files%20(x86)/Eclipse%20Adoptium/jdk-8.0.452.9-hotspot", - }, - }), + # Put jvmfwk in DIRECT mode with an explicit, arch-matched JRE — see + # //build:jre.bzl for why that is a select() and not $JAVA_HOME. + } | jre_home_env(), uno_install = "//main/staging:install", ) diff --git a/main/testtools/BUILD.bazel b/main/testtools/BUILD.bazel new file mode 100644 index 0000000000..80e71c46a2 --- /dev/null +++ b/main/testtools/BUILD.bazel @@ -0,0 +1,420 @@ +package(default_visibility = ["//visibility:public"]) + +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") +load("@rules_java//java:defs.bzl", "java_import", "java_library") +load("//build:jre.bzl", "jre_home_env") +load("//build/rules:gtest_test.bzl", "staged_run_test") +load("//build/rules:idl_pipeline.bzl", "idl_library") +load("//build/rules:java_pipeline.bzl", "jar_from_directory", "javamaker_classes", "uno_jar") +load("//build/rules:merge_rdb.bzl", "merge_rdb") +load("//main/postprocess:postprocess.bzl", "services_rdb") + +# ── testtools / bridgetest ──────────────────────────────────────────────── +# The UNO bridge round-trip suite: every IDL construct the type system has — +# each simple type, strings, enums, structs, polymorphic structs, sequences, +# anys, interfaces, attributes, out/inout parameters, exceptions, multiple +# inheritance, the current context, recursive and sequence-of-calls dispatch — +# driven through a call chain and checked on the way back. +# +# Unlike every other test in this tree it is NOT a GoogleTest binary. It is +# three UNO COMPONENTS, and the runner is the generic //main/cpputools uno.exe: +# +# uno -ro uno_services.rdb -ro uno_types.rdb \ +# -s com.sun.star.test.bridge.BridgeTest -- com.sun.star.test.bridge.CppTestObject +# +# uno.exe instantiates the service named by -s, casts it to XMain, and calls +# run() with everything after "--" as arguments. BridgeTest::run() therefore +# receives the NAME of the object to test and instantiates it itself, which is +# what makes one driver reusable across the C++, Java, Python and CLI test +# objects — only that last argument changes. Its exit code is the test result, +# so no gtest wrapper is needed or wanted (source changes are out of scope). +# +# This is a self-contained UNO installation, NOT fixture (a): uno.exe's -ro +# path calls bootstrap_InitialComponentContext(registry), not +# defaultBootstrap_InitialComponentContext(), so there is no fundamental.ini +# and no staged office involved. Everything the service manager needs beyond +# the two registries below is hardcoded in cppuhelper's +# bootstrapInitialSF() — ORegistryServiceManager, DLLComponentLoader, +# SimpleRegistry, NestedRegistry, TypeDescriptionManager and the rest, all +# loaded out of bootstrap.uno.dll beside the exe. Hence no uno_install, and +# hence the test runs in seconds rather than depending on the whole office. + +_DEFINES = [ + "WNT", + "GUI", + "WIN32", + "WINVER=0x0500", + "_WIN32_WINNT=0x0500", + "_WIN32_IE=0x0500", + "FULL_DESK", +] + +_COPTS = [ + # sal_Unicode is not VS2008's native wchar_t; every UNO string type depends + # on the two agreeing. + "/Zc:wchar_t-", + "/EHsc", + "/Imain/testtools/inc/pch", +] + +_DEPS = [ + "//main/sal:sal_headers", + "//main/cppu:cppu_headers", + "//main/cppuhelper:cppuhelper_headers", + "//main/udkapi:udkapi_idl_headers", + # osl/diagnose.hxx (the C++ header, as opposed to diagnose.h) declares an + # std::hash_set member, which VS2008's own STL does not have. + "//main/stlport:stlport", + ":bridgetest_idl_headers", +] + +_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)", + "/MANIFEST:NO", +] + +# ── The suite's private IDL ─────────────────────────────────────────────── +# test::testtools::bridgetest — the types actually under test. It is a +# deliberately exhaustive vocabulary, which is the whole point: a bridge that +# mishandles one type class fails here and nowhere else. +idl_library( + name = "bridgetest_idl", + srcs = ["source/bridgetest/idl/bridgetest.idl"], + include_dirs = ["main/udkapi"], + extra_rdbs = ["//main/udkapi:udkapi_idl"], + testonly = True, +) + +cc_library( + name = "bridgetest_idl_headers", + hdrs = [":bridgetest_idl"], + includes = ["bridgetest_idl_inc"], + testonly = True, +) + +# The type registry uno.exe is given. dmake copies udkapi.rdb and regmerges +# bridgetest.rdb into it at "/"; merge_rdb does both in one action. Both +# halves are needed — the private types reference com.sun.star.uno.XInterface +# and friends, and the TypeDescriptionManager resolves them at run time. +merge_rdb( + name = "bridgetest_uno_types", + out = "uno_types.rdb", + rdbs = [ + "//main/udkapi:udkapi_idl", + ":bridgetest_idl", + ], + testonly = True, +) + +# ── The three components ────────────────────────────────────────────────── +# cppobj.uno — the object UNDER test: com.sun.star.test.bridge.CppTestObject, +# a C++ implementation of XBridgeTest2 (and XMulti, and the current-context +# checker). This is what the driver bounces every type off. +cc_binary( + name = "cppobj.uno", + srcs = [ + "source/bridgetest/cppobj.cxx", + "source/bridgetest/currentcontextchecker.cxx", + "source/bridgetest/currentcontextchecker.hxx", + "source/bridgetest/multi.cxx", + "source/bridgetest/multi.hxx", + ], + copts = _COPTS, + defines = _DEFINES, + linkshared = True, + win_def_file = "source/bridgetest/cppobj.uno.def", + deps = _DEPS, + additional_linker_inputs = _LINKER_INPUTS, + linkopts = _LINKOPTS, + testonly = True, +) + +# bridgetest.uno — the DRIVER: com.sun.star.test.bridge.BridgeTest, an XMain +# whose run() performs the round trip and returns 0 on success. It links its +# own copies of multi/currentcontextchecker because both sides of the +# comparison need them (dmake builds two static libs from the same objects). +cc_binary( + name = "bridgetest.uno", + srcs = [ + "source/bridgetest/bridgetest.cxx", + "source/bridgetest/currentcontextchecker.cxx", + "source/bridgetest/currentcontextchecker.hxx", + "source/bridgetest/multi.cxx", + "source/bridgetest/multi.hxx", + ], + copts = _COPTS, + defines = _DEFINES, + linkshared = True, + win_def_file = "source/bridgetest/bridgetest.uno.def", + deps = _DEPS, + additional_linker_inputs = _LINKER_INPUTS, + linkopts = _LINKOPTS, + testonly = True, +) + +# constructors.uno — covers IDL service CONSTRUCTORS specifically (the +# "new-style" services with named constructors and a rest parameter), which +# the driver exercises through test.testtools.bridgetest.Constructors{,2}. +cc_binary( + name = "constructors.uno", + srcs = ["source/bridgetest/constructors.cxx"], + copts = _COPTS, + defines = _DEFINES, + linkshared = True, + win_def_file = "source/bridgetest/constructors.uno.def", + deps = _DEPS, + additional_linker_inputs = _LINKER_INPUTS, + linkopts = _LINKOPTS, + testonly = True, +) + +# The service registry. The "./<dll>" URIs are dmake's, verbatim, and they are +# the right choice rather than the vnd.sun.star.expand: form used elsewhere: +# stoc's textualservices.cxx resolves a component uri with +# rtl::Uri::convertRelToAbs() against the URL of the registry FILE, so a +# relative URI simply means "beside this rdb" and needs no bootstrap variable +# to point at the staged directory. +services_rdb( + name = "bridgetest_uno_services", + components = { + "source/bridgetest/bridgetest.component": "./bridgetest.uno.dll", + "source/bridgetest/cppobj.component": "./cppobj.uno.dll", + "source/bridgetest/constructors.component": "./constructors.uno.dll", + }, + out = "uno_services.rdb", + xslt = "//main/solenv/bin:packcomponents.xslt", + testonly = True, +) + +# ── The test ────────────────────────────────────────────────────────────── +# dmake's `runtest` target, which the ordinary build runs unconditionally on +# every platform whose bridge is believed to work (see the OSX-PPC/Solaris +# escape hatch in source/bridgetest/makefile.mk). +# +# The DLL closure is listed explicitly instead of borrowing the office's via +# uno_install, because it is small and known: uno.exe links sal/cppu/ +# cppuhelper/salhelper, cppuhelper loads bootstrap.uno.dll from its OWN +# directory, and bootstrap.uno pulls reg (→ store) and xmlreader. Co-locating +# them is correct HERE — the "co-located UNO DLL breaks the bootstrap" +# landmine is specifically about doing it *alongside* uno_install, where +# get_this_libpath() would then pick the test dir over program/. +# +# The bridge DLL is a pure runtime dependency that nothing links: any cppu +# Mapping osl_loadModule()s it, and the driver's exception round-trip goes +# through cppu::getCaughtException(). Unstaged it is an empty test.log. +staged_run_test( + name = "bridgetest", + binary = "//main/cpputools:uno", + # Bare relative registry names, and the working directory set to the staged + # dir — exactly dmake's `cd $(DLLDEST) && uno -ro uno_services.rdb …`. It + # also keeps the relative component URIs above meaningful. + run_args = [ + "-ro", + "uno_services.rdb", + "-ro", + "uno_types.rdb", + "-s", + "com.sun.star.test.bridge.BridgeTest", + "--", + "com.sun.star.test.bridge.CppTestObject", + ], + run_in_staged_dir = True, + runtime = [ + ":bridgetest.uno", + ":constructors.uno", + ":cppobj.uno", + ":bridgetest_uno_services", + ":bridgetest_uno_types", + "//main/cppu:cppu3", + "//main/cppuhelper:cppuhelper3MSC", + "//main/external/msvcp90:crt_dlls", + "//main/registry:reg", + "//main/sal:sal3", + "//main/salhelper:salhelper3MSC", + "//main/stoc:bootstrap.uno", + "//main/store:store", + "//main/xmlreader:xmlreader", + ] + select({ + "//build:arch_x64": ["//main/bridges:mscx_uno"], + "//conditions:default": ["//main/bridges:msci_uno"], + }), + size = "small", +) + +# ── The same round trip, through Java ───────────────────────────────────── +# dmake's `bridgetest_inprocess_java`. The driver, the assertions and the IDL +# are all unchanged; only the object on the far side is swapped: +# +# -s com.sun.star.test.bridge.BridgeTest -- com.sun.star.test.bridge.JavaTestObject +# +# so every type in bridgetest.idl now crosses the java_uno JNI bridge twice +# (out and back) instead of staying in C++. That makes this by far the widest +# exercise of the Java bridge in the tree — cppuhelper/qa/propertysetmixin +# proved a Java component can be LOADED at all; this one checks that the +# marshalling is actually correct for every type class. +# +# "noCurrentContext" is the second argument the dmake recipe passes, and it is +# not optional: the Java side has no XCurrentContext implementation to hand +# back, so the driver skips that one sub-test (bridgetest.cxx run()). + +javamaker_classes( + name = "bridgetest_java_classes", + rdb = ":bridgetest_idl", + extra_rdb = "//main/udkapi:udkapi_idl", + testonly = True, +) + +jar_from_directory( + name = "bridgetest_java_types_jar", + classes_dir = ":bridgetest_java_classes", + testonly = True, +) + +java_import( + name = "bridgetest_java_types", + jars = [":bridgetest_java_types_jar"], + testonly = True, +) + +# JARFILES in com/sun/star/comp/bridge/makefile.mk is ridl.jar jurt.jar juh.jar. +# ridl covers both com.sun.star.uno.* and the udkapi types the object +# implements beyond its own IDL — com.sun.star.test.performance.XPerformanceTest +# (udkapi/com/sun/star/test/performance), which TestComponent also implements. +java_library( + name = "bridgetest_testcomponent", + srcs = glob(["com/sun/star/comp/bridge/*.java"]), + javacopts = ["--release", "8", "-XepDisableAllChecks"], + deps = [ + ":bridgetest_java_types", + "//main/javaunohelper:juh_jar", + "//main/jurt:jurt", + "//main/ridljar:ridl", + ], + testonly = True, +) + +# Both class trees in one jar, as JARCLASSDIRS = $(PACKAGE) test/testtools/ +# bridgetest does. RegistrationClassName is copied from the checked-in +# com/sun/star/comp/bridge/manifest — it is how javaloader finds the class to +# register, so it has to be a main manifest attribute, which is what uno_jar is +# for (a plain java_library cannot express it). +uno_jar( + name = "bridgetest_testcomponent_jar", + out = "testComponent.jar", + jars = [ + ":bridgetest_testcomponent", + ":bridgetest_java_types_jar", + ], + manifest_lines = [ + "RegistrationClassName: com.sun.star.comp.bridge.TestComponent", + ], + testonly = True, +) + +# Only the test's OWN components. The Java run additionally needs the two +# halves of com.sun.star.loader.Java2 — javavm.uno (locates and starts a JVM +# through jvmfwk) and javaloader.uno (builds a class loader over the jar and +# calls its RegistrationClassName) — and those come, as in dmake, from a THIRD +# registry: the installation's own services.rdb, nested in below. +# +# Registering them here instead does not work, and the reason is worth keeping: +# javaloader resolves the component URL through +# com.sun.star.uri.UriReferenceFactory, which is itself a URE component (stoc's +# uriproc). Naming the loaders without the registry that provides everything +# they in turn need just moves the failure one service along. +services_rdb( + name = "bridgetest_uno_services_java", + components = { + "source/bridgetest/bridgetest.component": "./bridgetest.uno.dll", + "source/bridgetest/cppobj.component": "./cppobj.uno.dll", + "source/bridgetest/constructors.component": "./constructors.uno.dll", + "source/bridgetest/testComponent.component": "./testComponent.jar", + }, + out = "uno_services_java.rdb", + xslt = "//main/solenv/bin:packcomponents.xslt", + testonly = True, +) + +# uno_install, unlike the C++ sibling — and here it earns its keep for the DATA +# rather than the DLLs. The Java2 loader's whole configuration lives in the +# staged program/: URE_INTERNAL_JAVA_DIR (the five jars javavm.cxx opens by +# HARDCODED NAME, starting with unoloader.jar), URE_MORE_JAVA_TYPES, +# URE_INTERNAL_JAVA_CLASSPATH and URE_OVERRIDE_JAVA_JFW_{SHARED,USER}_DATA are +# all keys in program/fundamental.ini, and jvmfwk.dll finds javavendors.xml, +# jvmfwk3.ini, sunjavaplugin.dll and JREProperties.class beside itself there. +# Pointing URE_BOOTSTRAP at that one ini supplies the lot, with no list to keep +# in sync here. It is also why the two loader components above use +# $URE_INTERNAL_LIB_DIR rather than the "./" form: they come from program/. +staged_run_test( + name = "bridgetest_java", + binary = "//main/cpputools:uno", + run_args = [ + # dmake's third registry ($(SOLARXMLDIR)/ure/services.rdb, copied in as + # services.rdb). Given as a file:/// URL so unoexe's convertToFileUrl + # passes it through untouched instead of going via + # getFileURLFromSystemPath. + "-ro", + "file:///$(PROGRAM_URL)/services.rdb", + "-ro", + "uno_services_java.rdb", + "-ro", + "uno_types.rdb", + "-s", + "com.sun.star.test.bridge.BridgeTest", + "--", + "com.sun.star.test.bridge.JavaTestObject", + "noCurrentContext", + ], + # Keeps the cwd on the staged dir (not program/), so the bare registry + # names above and the "./" component URIs still resolve. + run_in_staged_dir = True, + # ONLY the test-only artifacts — the exact INVERSE of the C++ sibling, and + # the "co-located UNO DLL breaks the bootstrap" landmine is why. Its + # mechanism is precise: cppu::get_unorc() (cppuhelper/source/bootstrap.cxx) + # opens uno.ini at get_this_libpath() + "/uno.ini", i.e. beside + # cppuhelper3MSC.dll, and uno.ini is where URE_INTERNAL_LIB_DIR is defined — + # fundamental.ini does not define it. Every vnd.sun.star.expand: URI in the + # tree goes through that handle (cppu::bootstrap_expandUri → + # cppuhelper::detail::expandMacros). Co-locate cppuhelper3MSC.dll and the + # exe's own directory wins the loader search, so the lookup lands in the + # test dir, finds no uno.ini, and every such macro silently expands to + # nothing. Here that surfaced as jvmfwk reporting + # The file: vnd.sun.star.expand:$URE_INTERNAL_LIB_DIR/sunjavaplugin.dll + # does not exist + # and then the catch-all "The JRE … could not be recognized". Leave the + # core stack to program/ via PATH and it resolves. + runtime = [ + ":bridgetest.uno", + ":constructors.uno", + ":cppobj.uno", + ":bridgetest_testcomponent_jar", + ":bridgetest_uno_services_java", + ":bridgetest_uno_types", + "//main/external/msvcp90:crt_dlls", + ], + env = {} | jre_home_env(), + uno_install = "//main/staging:install", + # Starting a JVM sounds like it should need "medium", but it does not: this + # is jvmfwk DIRECT mode over a tiny classpath, measured at ~1.4s, and + # //main/cppuhelper:cppuhelper_qa_propertysetmixin runs "small" for the same + # reason. Only office_connection (a real soffice boot) needs "medium". + size = "small", +) + +# Both halves of the round trip: the same driver and the same assertions, once +# against a C++ object and once against a Java one. +test_suite( + name = "bridgetest_tests", + tests = [ + ":bridgetest", + ":bridgetest_java", + ], +) diff --git a/main/testtools/readme.md b/main/testtools/readme.md new file mode 100644 index 0000000000..23953ac06d --- /dev/null +++ b/main/testtools/readme.md @@ -0,0 +1,183 @@ +<!-- + 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 testtools (bridgetest: C++ and Java green) + +`bridgetest` is the UNO bridge round-trip suite: every construct the type +system has — each simple type, strings, enums, structs, polymorphic structs, +sequences, anys, interfaces, attributes, out/inout parameters, exceptions, +multiple inheritance, the current context, recursive and sequence-of-calls +dispatch — pushed through a call chain and checked coming back. + +Two targets, one driver: + +| target | object under test | fixture | time | +| --- | --- | --- | --- | +| `//main/testtools:bridgetest` | `CppTestObject` (C++) | self-contained | ~1.1 s | +| `//main/testtools:bridgetest_java` | `JavaTestObject` (Java) | `uno_install` | ~1.4 s | + +`//main/testtools:bridgetest_tests` runs both. + +## It is not a GoogleTest, and should not be made into one + +Every other test in this tree is a gtest binary. This one is three UNO +*components*, run by the generic `//main/cpputools` `uno.exe`: + +``` +uno -ro uno_services.rdb -ro uno_types.rdb \ + -s com.sun.star.test.bridge.BridgeTest -- com.sun.star.test.bridge.CppTestObject +``` + +`uno.exe` instantiates the service named by `-s`, queries `XMain`, and calls +`run()` with everything after `--`. So the driver receives the *name* of the +object to test and instantiates it itself — which is exactly what lets one +driver serve the C++, Java, Python and CLI test objects. Only that last +argument changes between the two targets above. `run()`'s return value is the +process exit code, so the pass/fail signal needs no wrapper, and wrapping it +would mean changing source, which is out of scope. + +This is what `run_args` on `staged_run_test` was added for +(`build/rules/gtest_test.bzl`): a fixed command line baked into the launcher +and token-expanded like `env`, so the "test binary" can be a generic tool that +a suite selects behaviour from. Anything passed on the `bazel test` command +line still follows it via `%*`. + +## The C++ target is a fourth fixture kind + +Not fixture (a): `uno.exe`'s `-ro` path calls +`bootstrap_InitialComponentContext(registry)`, **not** +`defaultBootstrap_InitialComponentContext()`. There is no `fundamental.ini`, no +`URE_BOOTSTRAP`, and no staged office — hence no `uno_install`, and hence a test +that runs in about a second. Everything the service manager needs beyond the two +registries is hardcoded in cppuhelper's `bootstrapInitialSF()` +(`ORegistryServiceManager`, `DLLComponentLoader`, `SimpleRegistry`, +`NestedRegistry`, `TypeDescriptionManager`, …), all loaded out of +`bootstrap.uno.dll` beside the exe. + +Two consequences worth knowing before adding another registry-bootstrap test: + +- **The textual (XML) registry works here**, unlike in `configmgr/qa/unit`. + `openRegistry()` passes `(bReadOnly=true, bCreate=false)`, and + `SimpleRegistry::open()`'s textual branch requires exactly + `bReadOnly && !bCreate`. configmgr's suite is red because it goes through the + one-argument `createRegistryServiceFactory` overload, which defaults + `bReadOnly=false`. Same class of registry, opposite outcome, decided by two + booleans. +- **Relative component URIs are the right choice**, not + `vnd.sun.star.expand:`. stoc's `textualservices.cxx` resolves a component + `uri` with `rtl::Uri::convertRelToAbs()` against the URL of the registry + *file*, so dmake's `./cppobj.uno.dll` just means "beside this rdb" and needs + no bootstrap variable pointing at the staged directory. That is why the C++ + target sets no `env` at all. + +## The Java target, and the landmine that inverts the DLL rule + +`bridgetest_java` swaps in `com.sun.star.test.bridge.JavaTestObject` from +`testComponent.jar`, so every type in `bridgetest.idl` now crosses the +`java_uno` JNI bridge twice. `cppuhelper/qa/propertysetmixin` proved a Java UNO +component can be *loaded*; this is the first thing to check that the +marshalling is correct across the whole type vocabulary. + +`noCurrentContext` is the second argument dmake passes and it is not optional — +the Java side has no `XCurrentContextChecker` to hand back, so the driver skips +that sub-test. + +Three things differ from the C++ target, and each cost a debugging round: + +**1. `$URE_INTERNAL_LIB_DIR` is not in `fundamental.ini`.** Registering the +Java2 loader halves with that macro yields the amputated +`loading component library failed: /javaloader.uno.dll` — an undefined +bootstrap macro expands to nothing rather than failing. The tree's convention +is `$OOO_BASE_DIR/program/…` (`basis_native()`), and that macro *is* defined. + +**2. Co-locating the core UNO DLLs breaks the bootstrap — and the mechanism is +`uno.ini`, not just `bootstrap.uno.dll`.** The existing note about +`get_this_libpath()` covers only half of it. `cppu::get_unorc()` +(`cppuhelper/source/bootstrap.cxx`) opens + +``` +get_this_libpath() + "/" + SAL_CONFIGFILE("uno") +``` + +that is, `uno.ini` **beside `cppuhelper3MSC.dll`** — and `uno.ini` is where +`URE_INTERNAL_LIB_DIR` is defined. *Every* `vnd.sun.star.expand:` URI in the +tree resolves through that one handle (`cppu::bootstrap_expandUri` → +`cppuhelper::detail::expandMacros`). Stage `cppuhelper3MSC.dll` next to the exe +and the exe's own directory wins the loader search, so the lookup lands in the +test directory, finds no `uno.ini`, and every such macro silently expands to +nothing. Here it surfaced two layers away, as + +``` +[Java framework] The file: vnd.sun.star.expand:$URE_INTERNAL_LIB_DIR/sunjavaplugin.dll does not exist. +[Java framework] The JRE specified by … could not be recognized. +``` + +So with `uno_install`, `runtime` must list only genuinely test-only files. The +C++ target lists the whole core stack and the Java one lists none — the +opposite of the usual advice, and correct in both cases. + +**3. The third registry is not optional.** dmake adds +`$(SOLARXMLDIR)/ure/services.rdb` and it is tempting to replace that with just +the two Java2-loader components. That does not work: `javaloader` resolves the +component URL through `com.sun.star.uri.UriReferenceFactory`, itself a URE +component (stoc's `uriproc`), so naming the loaders without the registry +providing what *they* need only moves the failure one service along. The +installation's `program/services.rdb` is nested in as a `file:///` URL, which +`unoexe`'s `convertToFileUrl` passes through untouched. + +`uno_install` here earns its keep for the **data**, not the DLLs: the Java2 +loader's entire configuration (`URE_INTERNAL_JAVA_DIR` and the five jars +`javavm.cxx` opens by hardcoded name, `URE_MORE_JAVA_TYPES`, +`URE_INTERNAL_JAVA_CLASSPATH`, `URE_OVERRIDE_JAVA_JFW_{SHARED,USER}_DATA`) is +keys in `program/fundamental.ini`, and `jvmfwk.dll` finds `javavendors.xml`, +`jvmfwk3.ini`, `sunjavaplugin.dll` and `JREProperties.class` beside itself +there. One `URE_BOOTSTRAP` supplies the lot with no list to keep in sync. + +## Build notes + +- `/Zc:wchar_t-` throughout — `sal_Unicode` is not VS2008's native `wchar_t`. +- `//main/stlport:stlport` — `osl/diagnose.hxx` (the C++ header, unlike + `diagnose.h`) declares an `std::hash_set` member. +- Each component DLL exports **two** functions, not the usual three: none of + the three sources defines `component_canUnload`, so the `.def` files + converted from `solenv/src/component.map` list only + `component_getImplementationEnvironment` and `component_getFactory`. +- `bridgetest.uno` and `cppobj.uno` each compile their own copy of + `multi.cxx` / `currentcontextchecker.cxx`; dmake builds two static libs from + the same objects for the same reason. +- The JRE for the Java target comes from `//build:jre.bzl`, shared with + `cppuhelper/qa/propertysetmixin`. It is machine-specific and arch-selected — + the test loads `jvm.dll` into its own process, so a 32-bit build needs a + 32-bit JRE and `$JAVA_HOME` cannot answer for both arches. + +## Not migrated + +- `source/bridgetest/cli` (C#/VB round trip) — blocked on the `cli_ure` + bucket's C# and C++/CLI toolchains. +- `source/bridgetest/pyuno` — the Python variant of the same driver; reachable + now that pyuno is wired, not yet done. +- `source/performance` (`perftest.uno`) — a benchmark, not a correctness test. + Note `TestComponent.java` *does* implement + `com.sun.star.test.performance.XPerformanceTest`, whose IDL lives in udkapi, + so the Java half of it is already compiled here. +- `source/cliversioning`, `qa/cli`, `qa/cliversioning` — `cli_ure` bucket. +- `bridgetest_server` / `bridgetest_client` / `bridgetest_javaserver` — the + socket-URP variants of the same driver. They need an acceptor and a second + process; the in-process targets cover the marshalling, and URP itself is + covered by `//main/binaryurp` and `//main/test:test_qa_officeconnection`. diff --git a/main/testtools/source/bridgetest/bridgetest.uno.def b/main/testtools/source/bridgetest/bridgetest.uno.def new file mode 100644 index 0000000000..41a0d8d208 --- /dev/null +++ b/main/testtools/source/bridgetest/bridgetest.uno.def @@ -0,0 +1,11 @@ +; Windows DEF equivalent of solenv/src/component.map, which +; source/bridgetest/makefile.mk names as SHL2VERSIONMAP for this library. +; +; Two exports, not the usual three: bridgetest.cxx defines +; component_getImplementationEnvironment and component_getFactory but no +; component_canUnload, so listing it would be an unresolved-symbol link error. +LIBRARY bridgetest.uno.dll + +EXPORTS + component_getImplementationEnvironment + component_getFactory diff --git a/main/testtools/source/bridgetest/constructors.uno.def b/main/testtools/source/bridgetest/constructors.uno.def new file mode 100644 index 0000000000..b3aacfe1ea --- /dev/null +++ b/main/testtools/source/bridgetest/constructors.uno.def @@ -0,0 +1,11 @@ +; Windows DEF equivalent of solenv/src/component.map, which +; source/bridgetest/makefile.mk names as SHL3VERSIONMAP for this library. +; +; Two exports, not the usual three: constructors.cxx defines +; component_getImplementationEnvironment and component_getFactory but no +; component_canUnload, so listing it would be an unresolved-symbol link error. +LIBRARY constructors.uno.dll + +EXPORTS + component_getImplementationEnvironment + component_getFactory diff --git a/main/testtools/source/bridgetest/cppobj.uno.def b/main/testtools/source/bridgetest/cppobj.uno.def new file mode 100644 index 0000000000..6f1a5b1a76 --- /dev/null +++ b/main/testtools/source/bridgetest/cppobj.uno.def @@ -0,0 +1,11 @@ +; Windows DEF equivalent of solenv/src/component.map, which +; source/bridgetest/makefile.mk names as SHL1VERSIONMAP for this library. +; +; Two exports, not the usual three: cppobj.cxx defines +; component_getImplementationEnvironment and component_getFactory but no +; component_canUnload, so listing it would be an unresolved-symbol link error. +LIBRARY cppobj.uno.dll + +EXPORTS + component_getImplementationEnvironment + component_getFactory
