You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openoffice.apache.org by pe...@apache.org on 2026/08/14 05:24:59 UTC
(openoffice) 16/31: test(cppuhelper): wire qa/propertysetmixin — component DLL, private IDL, services.rdb
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 73a14824945df6b115f547dfa5624d650e7e5562
Author: Peter Kovacs <pe...@apache.org>
AuthorDate: Wed Aug 5 07:35:42 2026 +0200
test(cppuhelper): wire qa/propertysetmixin — component DLL, private IDL, services.rdb
The last of the three fixture consumers on the frontier, and the most involved
qa/ wiring so far: cppu::PropertySetMixin turns IDL [attribute]s into an
XPropertySet, and the only way to exercise that is through a real UNO component
that USES the mixin — so the suite ships one. Four artifacts, not one:
* idl_library qa/propertysetmixin/types.idl → .rdb + C++ headers
* merge_rdb re-emits just the registry as psm_types.rdb (idl_library
returns its .rdb AND a header directory)
* cc_binary qa_propertysetmixin.uno.dll, the component under test
* services_rdb registers it at
vnd.sun.star.expand:$OOO_INBUILD_SHAREDLIB_DIR/…
It is FIXTURE (a) — in-process bootstrap, no soffice — despite the makefile
sitting behind OOO_SUBSEQUENT_TESTS. The note in cppuhelper/BUILD.bazel
claiming it needed the OfficeConnection fixture was wrong and is corrected.
Unlike configmgr/qa/unit it uses the modern .component mechanism, so it does
not hit that suite's component_writeInfo wall.
LANDMINE: rtl::Bootstrap has no "append", and it resolves the ENVIRONMENT
BEFORE the ini — so env UNO_TYPES/UNO_SERVICES REPLACE fundamental.ini's values
rather than adding to them, and must repeat the install's entries (including
oovbaapi.rdb, easy to miss) before appending the test's own. Drift watch noted
against main/staging/fundamental.ini; the symptom of forgetting one is a
service the office normally provides going missing.
The .def carries TWO exports, not the usual three: comp_propertysetmixin.cxx
defines no component_canUnload, so listing it would fail to link.
Two small gtest_test generalizations came out of it, both reusable:
* $(RUNDIR_URL) / $(PROGRAM_URL) alongside $(SCRATCH_URL) — forward-slash
forms for building file:/// URLs, which is what every UNO bootstrap
variable wants and which sidesteps the backslash-escaping trap entirely;
* data_tree now disambiguates a multi-file provider by the BASENAME you asked
to stage it as, instead of demanding exactly one file. services_rdb also
emits services.input and idl_library also emits a header dir, so the strict
form would have needed filegroup boilerplate at every call site.
EXPECTED 3 of 6 RED: testJavaEmpty1/testJavaEmpty2/testJavaFull need the
suite's OTHER component, a Java one (JavaSupplier.java +
qa_propertysetmixin.java.component + a .uno.jar built by javamaker), which
belongs to the Java bucket. They surface as a bare "Unknown C++ exception"
because UNO exceptions do not derive from std::exception — the expected shape
of the miss, not a crash. The three C++ cases are the ones that actually
exercise PropertySetMixin; the Java half turns green for free later.
Verified: analysis clean, and the IDL, both registries, the component DLL and
the test exe all build. The run itself needs //main/staging:install.
Co-Authored-By: Claude Opus 5 <no...@anthropic.com>
---
CLAUDE.md | 27 +++-
build/rules/gtest_test.bzl | 33 ++++-
main/cppuhelper/BUILD.bazel | 137 ++++++++++++++++++++-
.../propertysetmixin/qa_propertysetmixin.uno.def | 11 ++
main/cppuhelper/readme.md | 100 +++++++++++++++
5 files changed, 296 insertions(+), 12 deletions(-)
diff --git a/CLAUDE.md b/CLAUDE.md
index 938985834d..ff38e03a75 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -189,10 +189,29 @@ test 🔨 C++ unit-test infra runnable — NOW THE FRONT-LINE TASK: br
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.
+ • cppuhelper/qa/propertysetmixin — WIRED 2026-08-05, builds clean,
+ RUN NOT YET CONFIRMED. It is fixture (a) (in-process bootstrap,
+ NO soffice) despite living behind OOO_SUBSEQUENT_TESTS — the old
+ note in cppuhelper/BUILD.bazel calling it an OfficeConnection test
+ was wrong. Uses the MODERN .component mechanism, so it does NOT
+ hit the configmgr component_writeInfo wall. Most involved qa/
+ wiring so far — FOUR artifacts, not one: idl_library
+ (qa/propertysetmixin/types.idl → headers) + merge_rdb (re-emits
+ just the registry as psm_types.rdb, since idl_library returns rdb
+ AND a header dir) + the component DLL qa_propertysetmixin.uno
+ (2-export DEF — comp_propertysetmixin.cxx has NO
+ component_canUnload) + services_rdb registering it at
+ vnd.sun.star.expand:$OOO_INBUILD_SHAREDLIB_DIR/… .
+ LANDMINE: rtl::Bootstrap has NO append and resolves the
+ ENVIRONMENT BEFORE the ini, so env UNO_TYPES/UNO_SERVICES REPLACE
+ fundamental.ini's and must REPEAT them (incl. oovbaapi.rdb) before
+ adding the test's own — DRIFT WATCH on main/staging/fundamental.ini.
+ EXPECTED 3/6 RED: testJava{Empty1,Empty2,Full} need the suite's
+ OTHER component, a Java one (JavaSupplier.java + .uno.jar via
+ javamaker) = Java bucket; they surface as "Unknown C++ exception"
+ (UNO exceptions don't derive from std::exception). The 3 C++ cases
+ are the ones that actually exercise PropertySetMixin.
+ See main/cppuhelper/readme.md.
• 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 2ed6785146..2c51dae546 100644
--- a/build/rules/gtest_test.bzl
+++ b/build/rules/gtest_test.bzl
@@ -64,12 +64,20 @@ def _windows_relpath(from_dir, to_dir):
# $(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
+# and a forward-slash variant of each, for building a `file:///` URL:
+# $(RUNDIR_URL) $(PROGRAM_URL) $(SCRATCH_URL)
# Everything a test writes belongs under $(SCRATCH): the staged dir lives in
# bazel-out and must be treated as read-only build output.
+#
+# Prefer the _URL forms wherever the consumer takes a URL — UNO bootstrap
+# variables all do. Forward slashes sidestep the backslash-escaping trap in
+# rtl::Bootstrap entirely (see the arg-user comment further down).
_TOKENS = [
- # $(SCRATCH_URL) FIRST — otherwise $(SCRATCH) matches its prefix.
+ # The _URL forms MUST come first — otherwise the plain token matches their
+ # prefix and leaves a stray "_URL" behind.
("$(SCRATCH_URL)", "%_SCRATCHU%"),
+ ("$(RUNDIR_URL)", "%_RUNU%"),
+ ("$(PROGRAM_URL)", "%_PROGU%"),
("$(SCRATCH)", "%_SCRATCH%"),
("$(RUNDIR)", "%_RUN%"),
("$(PROGRAM)", "%_PROG%"),
@@ -158,9 +166,19 @@ def _staged_gtest_test_impl(ctx):
# 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()
+ want = rel.split("/")[-1]
+ if len(files) > 1:
+ # Several rules legitimately return more than the one artifact you
+ # want (services_rdb also emits services.input; idl_library emits
+ # its .rdb plus a header directory). Disambiguate by the basename
+ # you asked to stage it AS, which is unambiguous and needs no extra
+ # filegroup boilerplate at every call site.
+ files = [f for f in files if f.basename == want]
if len(files) != 1:
- fail("data_tree entry %s must provide exactly one file, got %d" %
- (label.label, len(files)))
+ fail(("data_tree entry %s → %s: expected one file, got %d. Give " +
+ "the staged path the same basename as the artifact you " +
+ "want, or point at a target that provides a single file.") %
+ (label.label, rel, len(files)))
o = ctx.actions.declare_file(d + "/" + rel)
ctx.actions.symlink(output = o, target_file = files[0])
staged.append(o)
@@ -234,7 +252,11 @@ def _staged_gtest_test_impl(ctx):
# %~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"']
+ lines += [
+ 'for %%I in ("%~dp0.") do set "_RUN=%%~fI"',
+ # file:/// URL form: on Windows only the separators differ.
+ 'set "_RUNU=%_RUN:\\=/%"',
+ ]
if need_scratch:
# Everything the test writes goes here. Wiped BEFORE as well as
@@ -259,6 +281,7 @@ def _staged_gtest_test_impl(ctx):
# 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"',
+ 'set "_PROGU=%_PROG:\\=/%"',
]
# Which installation the UNO bootstrap describes. Normally the
diff --git a/main/cppuhelper/BUILD.bazel b/main/cppuhelper/BUILD.bazel
index be943453a4..6b588be016 100644
--- a/main/cppuhelper/BUILD.bazel
+++ b/main/cppuhelper/BUILD.bazel
@@ -2,7 +2,9 @@ package(default_visibility = ["//visibility:public"])
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
load("//build/rules:idl_pipeline.bzl", "idl_library")
+load("//build/rules:merge_rdb.bzl", "merge_rdb")
load("//build/rules:gtest_test.bzl", "gtest_test")
+load("//main/postprocess:postprocess.bzl", "services_rdb")
_COPTS = [
"/Zm500", "/Zc:forScope", "/GR", "/nologo", "/Gs",
@@ -128,9 +130,10 @@ filegroup(
# The dmake makefiles also list $(TESTSHL2LIB) — a no-op, the variable is
# undefined since AOO retired testshl2 (see main/test/readme.md).
#
-# NOT wired: qa/propertysetmixin — it is a *subsequent* test (builds a UNO
-# component DLL + its own types.idl, driven from Java against a service
-# manager), so it needs the OfficeConnection fixture, not a standalone exe.
+# qa/propertysetmixin is wired at the BOTTOM of this file — it needs its own
+# component DLL, private types and services.rdb, so it does not fit these
+# shared _QA_* constants. (It is NOT an OfficeConnection test, as an earlier
+# note here claimed: it bootstraps in-process and launches no soffice.)
_QA_COPTS = ["/Imain/cppuhelper/inc/pch"]
_QA_DEPS = [
@@ -224,3 +227,131 @@ test_suite(
":cppuhelper_qa_weak",
],
)
+
+# ── qa/propertysetmixin ──────────────────────────────────────────────────
+# cppu::PropertySetMixin — the helper that turns IDL [attribute]s into a
+# com.sun.star.beans.XPropertySet. The only way to test it is through a real
+# UNO component that USES the mixin, so the suite ships one, plus the private
+# IDL the component implements, plus a services.rdb registering it.
+#
+# Fixture (a): an in-process bootstrap, no soffice — despite the makefile
+# living behind OOO_SUBSEQUENT_TESTS. Unlike configmgr/qa/unit it uses the
+# MODERN .component registration mechanism, so it does not hit that suite's
+# component_writeInfo wall (see main/configmgr/readme.md).
+#
+# The dmake recipe sets UNO_TYPES / UNO_SERVICES / URE_INTERNAL_LIB_DIR /
+# OOO_INBUILD_SHAREDLIB_DIR by hand. Two of those come free from
+# uno_install's URE_BOOTSTRAP, but UNO_TYPES and UNO_SERVICES must still be set
+# explicitly because the test's OWN registries have to be APPENDED to the
+# install's — rtl::Bootstrap resolves the environment before the ini, so the
+# values below win over fundamental.ini and therefore have to repeat the
+# install's entries rather than just adding to them.
+
+idl_library(
+ name = "cppuhelper_qa_psm_idl",
+ srcs = ["qa/propertysetmixin/types.idl"],
+ include_dirs = ["main/udkapi"],
+ extra_rdbs = ["//main/udkapi:udkapi_idl"],
+)
+
+cc_library(
+ name = "cppuhelper_qa_psm_idl_headers",
+ hdrs = [":cppuhelper_qa_psm_idl"],
+ includes = ["cppuhelper_qa_psm_idl_inc"],
+ testonly = True,
+)
+
+# idl_library returns its .rdb AND a header directory; merge_rdb re-emits just
+# the registry, under the name the test stages it as.
+merge_rdb(
+ name = "cppuhelper_qa_psm_types",
+ rdbs = [":cppuhelper_qa_psm_idl"],
+ out = "psm_types.rdb",
+ testonly = True,
+)
+
+# The component under test. Two DEF exports, not the usual three:
+# comp_propertysetmixin.cxx has no component_canUnload (see the .def).
+cc_binary(
+ name = "qa_propertysetmixin.uno",
+ srcs = ["qa/propertysetmixin/comp_propertysetmixin.cxx"],
+ copts = _QA_COPTS + ["/Zc:wchar_t-"],
+ defines = _DEFINES,
+ linkshared = True,
+ win_def_file = "qa/propertysetmixin/qa_propertysetmixin.uno.def",
+ deps = _QA_DEPS + [":cppuhelper_qa_psm_idl_headers"],
+ additional_linker_inputs = _QA_LINKER_INPUTS,
+ linkopts = _QA_LINKOPTS + ["/MANIFEST:NO"],
+ testonly = True,
+)
+
+# $OOO_INBUILD_SHAREDLIB_DIR is the dmake COMPONENTPREFIX_INBUILD_NATIVE
+# (solenv/inc/settings.mk) — "the component lives wherever this build put it",
+# resolved by the env var below to the staged test dir.
+services_rdb(
+ name = "cppuhelper_qa_psm_services",
+ components = {
+ "qa/propertysetmixin/qa_propertysetmixin.cpp.component":
+ "vnd.sun.star.expand:$OOO_INBUILD_SHAREDLIB_DIR/qa_propertysetmixin.uno.dll",
+ },
+ out = "psm_services.rdb",
+ xslt = "//main/solenv/bin:packcomponents.xslt",
+ testonly = True,
+)
+
+# EXPECTED RED: 3 of 6. testJavaEmpty1/testJavaEmpty2/testJavaFull ask for
+# test.cppuhelper.propertysetmixin.JavaSupplier, which lives in the suite's
+# OTHER component — a Java one (JavaSupplier.java + qa_propertysetmixin.java.
+# component + a .uno.jar built by javamaker). That belongs to the Java bucket
+# and is not wired, so those three fail to instantiate the service. The three
+# C++ cases are the ones that actually exercise PropertySetMixin; wiring the
+# suite now means the mixin is covered, and the Java half turns green later for
+# free. (UNO exceptions do not derive from std::exception, so gtest reports
+# the Java failures as a bare "Unknown C++ exception" — that is the expected
+# shape of the miss, not a crash.)
+gtest_test(
+ name = "cppuhelper_qa_propertysetmixin",
+ srcs = ["qa/propertysetmixin/test_propertysetmixin.cxx"],
+ copts = _QA_COPTS + ["/Zc:wchar_t-"],
+ defines = _DEFINES,
+ deps = _QA_DEPS + [
+ ":cppuhelper_qa_psm_idl_headers",
+ "//main/offapi:offapi_idl_headers",
+ ],
+ additional_linker_inputs = _QA_LINKER_INPUTS,
+ linkopts = _QA_LINKOPTS,
+ # The component DLL is test-only, so it is genuinely not in the install and
+ # must be staged. Co-locating it is safe — the "co-located UNO DLL breaks
+ # the bootstrap" landmine applies to cppuhelper3MSC and friends, whose
+ # directory cppuhelper resolves from its own module handle; this one is
+ # found only through the services.rdb URI below.
+ runtime_dlls = [":qa_propertysetmixin.uno"],
+ data_tree = {
+ ":cppuhelper_qa_psm_types": "psm_types.rdb",
+ ":cppuhelper_qa_psm_services": "psm_services.rdb",
+ },
+ env = {
+ # Space-separated URL lists. rtl::Bootstrap has no "append", and it
+ # resolves the environment BEFORE the ini, so these REPLACE
+ # fundamental.ini's values and must therefore repeat them.
+ #
+ # DRIFT: the leading entries mirror main/staging/fundamental.ini
+ # UNO_TYPES=${ORIGIN}/types.rdb ${ORIGIN}/oovbaapi.rdb
+ # UNO_SERVICES=${ORIGIN}/services.rdb
+ # (${ORIGIN} expands to that ini's own directory as a file URL, so the
+ # two forms agree). If that file gains an entry, add it here too — the
+ # symptom is a service the office normally provides going missing.
+ "UNO_TYPES": (
+ "file:///$(PROGRAM_URL)/types.rdb " +
+ "file:///$(PROGRAM_URL)/oovbaapi.rdb " +
+ "file:///$(RUNDIR_URL)/psm_types.rdb"
+ ),
+ "UNO_SERVICES": (
+ "file:///$(PROGRAM_URL)/services.rdb " +
+ "file:///$(RUNDIR_URL)/psm_services.rdb"
+ ),
+ # Where the vnd.sun.star.expand: URI in psm_services.rdb resolves to.
+ "OOO_INBUILD_SHAREDLIB_DIR": "file:///$(RUNDIR_URL)",
+ },
+ uno_install = "//main/staging:install",
+)
diff --git a/main/cppuhelper/qa/propertysetmixin/qa_propertysetmixin.uno.def b/main/cppuhelper/qa/propertysetmixin/qa_propertysetmixin.uno.def
new file mode 100644
index 0000000000..b33249bff5
--- /dev/null
+++ b/main/cppuhelper/qa/propertysetmixin/qa_propertysetmixin.uno.def
@@ -0,0 +1,11 @@
+; Windows DEF equivalent of solenv/src/component.map (SHL2VERSIONMAP in
+; qa/propertysetmixin/makefile.mk).
+;
+; Only TWO exports, not the usual three: comp_propertysetmixin.cxx defines
+; component_getFactory and component_getImplementationEnvironment but no
+; component_canUnload, so listing it would be an unresolved-symbol link error.
+LIBRARY qa_propertysetmixin.uno.dll
+
+EXPORTS
+ component_getImplementationEnvironment
+ component_getFactory
diff --git a/main/cppuhelper/readme.md b/main/cppuhelper/readme.md
new file mode 100644
index 0000000000..b17d124e2c
--- /dev/null
+++ b/main/cppuhelper/readme.md
@@ -0,0 +1,100 @@
+<!--
+ 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 cppuhelper (done)
+
+- `cppuhelper3MSC.dll`; private type `XExceptionThrower.idl` via `idl_library`
+ (`cppuhelper_unotypes_idl`, rooted at `unotypes/` so the generated header
+ path is `cppuhelper/detail/XExceptionThrower.hpp`)
+- Standalone qa suites: `cppuhelper_qa_ifcontainer` (5), `cppuhelper_qa_unourl`,
+ `cppuhelper_qa_weak`, gathered in `:cppuhelper_tests`
+
+## qa/propertysetmixin — `//main/cppuhelper:cppuhelper_qa_propertysetmixin`
+
+`cppu::PropertySetMixin` turns IDL `[attribute]`s into a
+`com.sun.star.beans.XPropertySet`. The only way to exercise it is through a real
+UNO component that *uses* the mixin, so the suite ships one — which is why this
+is the most involved qa/ wiring in the tree so far. Four artifacts, not one:
+
+| Target | What it is |
+| --- | --- |
+| `:cppuhelper_qa_psm_idl` | `qa/propertysetmixin/types.idl` → `.rdb` + C++ headers |
+| `:cppuhelper_qa_psm_types` | `merge_rdb` re-emitting just the registry as `psm_types.rdb` |
+| `:qa_propertysetmixin.uno` | the component DLL (`comp_propertysetmixin.cxx`) |
+| `:cppuhelper_qa_psm_services` | `services_rdb` registering that DLL → `psm_services.rdb` |
+
+`merge_rdb` is there only because `idl_library` returns its `.rdb` *and* a
+header directory; the merge re-emits the registry alone, under the name the test
+stages it as.
+
+**This is fixture (a)** — an in-process bootstrap, no soffice — despite the
+makefile sitting behind `OOO_SUBSEQUENT_TESTS`. An earlier note in
+`BUILD.bazel` claimed it needed `OfficeConnection`; it does not. Unlike
+`configmgr/qa/unit` it uses the **modern `.component` mechanism**, so it does
+not hit that suite's `component_writeInfo` wall (see
+`main/configmgr/readme.md`).
+
+### Environment
+
+The dmake recipe sets `UNO_TYPES`, `UNO_SERVICES`, `URE_INTERNAL_LIB_DIR` and
+`OOO_INBUILD_SHAREDLIB_DIR` by hand. The middle two come free from
+`uno_install`'s `URE_BOOTSTRAP`, but the registries must still be set
+explicitly, because the test's own have to be **appended** to the install's —
+and `rtl::Bootstrap` has no append. It resolves the environment *before* the
+ini, so these values **replace** `fundamental.ini`'s and therefore have to
+repeat them:
+
+ UNO_TYPES = <program>/types.rdb <program>/oovbaapi.rdb <rundir>/psm_types.rdb
+ UNO_SERVICES = <program>/services.rdb <rundir>/psm_services.rdb
+
+> **Drift watch:** the leading entries mirror `main/staging/fundamental.ini`.
+> If that file gains a registry, add it here too. The symptom of forgetting is
+> a service the office normally provides going missing.
+
+`OOO_INBUILD_SHAREDLIB_DIR` is dmake's `COMPONENTPREFIX_INBUILD_NATIVE`
+(`solenv/inc/settings.mk`) — "the component lives wherever this build put it".
+The `vnd.sun.star.expand:` URI in `psm_services.rdb` resolves through it to the
+staged test directory.
+
+Co-locating the component DLL is safe here: the "co-located UNO DLL breaks the
+bootstrap" landmine applies to `cppuhelper3MSC` and friends, whose directory
+cppuhelper resolves from its *own* module handle. This one is reached only
+through the services.rdb URI.
+
+### Expected: 3 of 6 red
+
+`testJavaEmpty1` / `testJavaEmpty2` / `testJavaFull` ask for
+`test.cppuhelper.propertysetmixin.JavaSupplier`, which lives in the suite's
+*other* component — a Java one (`JavaSupplier.java` +
+`qa_propertysetmixin.java.component` + a `.uno.jar` built by `javamaker`). That
+belongs to the Java bucket and is not wired, so those three cannot instantiate
+their service. UNO exceptions do not derive from `std::exception`, so gtest
+reports them as a bare "Unknown C++ exception" — that is the expected shape of
+the miss, not a crash.
+
+The three C++ cases are the ones that actually exercise `PropertySetMixin`.
+Wiring the suite now means the mixin is covered, and the Java half turns green
+for free once the Java bucket lands.
+
+### DEF file
+
+`qa/propertysetmixin/qa_propertysetmixin.uno.def` is the Windows equivalent of
+`solenv/src/component.map` (`SHL2VERSIONMAP`). **Two** exports, not the usual
+three: `comp_propertysetmixin.cxx` defines no `component_canUnload`, so listing
+it would be an unresolved-symbol link error.