From 4a73a7451d66077fecc32072932c4f7d40548179 Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Tue, 21 Jul 2026 09:23:36 +0800 Subject: [PATCH 1/7] Add WinRT regression test harness Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- crates/dynwinrt/tests/winrt_regression.rs | 406 ++++++++++++++++++++++ 1 file changed, 406 insertions(+) create mode 100644 crates/dynwinrt/tests/winrt_regression.rs diff --git a/crates/dynwinrt/tests/winrt_regression.rs b/crates/dynwinrt/tests/winrt_regression.rs new file mode 100644 index 00000000..da1e74d5 --- /dev/null +++ b/crates/dynwinrt/tests/winrt_regression.rs @@ -0,0 +1,406 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use dynwinrt::{InterfaceSignature, MetadataTable, MethodSignature, WinRTValue}; +use windows::Devices::Geolocation::{BasicGeoposition, Geopoint, IGeopoint, IGeopointFactory}; +use windows::Foundation::{IPropertyValue, IUriRuntimeClass, IUriRuntimeClassFactory}; +use windows::Win32::System::WinRT::{RO_INIT_MULTITHREADED, RoInitialize}; +use windows_core::{GUID, HRESULT, HSTRING, Interface}; + +fn init_winrt() { + // The process may already be initialized by another test; both success and + // already-initialized failures are acceptable for these headless WinRT calls. + let _ = unsafe { RoInitialize(RO_INIT_MULTITHREADED) }; +} + +fn assert_hstring(value: &WinRTValue, expected: &str) { + assert_eq!(value.as_hstring().expect("expected HSTRING"), expected); +} + +fn assert_bool(value: &WinRTValue, expected: bool) { + match value { + WinRTValue::Bool(actual) => assert_eq!(*actual, expected), + other => panic!("expected Bool({expected}), got {other:?}"), + } +} + +fn uri_runtime_class_signature(reg: &std::sync::Arc) -> InterfaceSignature { + let mut iface = InterfaceSignature::define_from_iinspectable( + "IUriRuntimeClass", + IUriRuntimeClass::IID, + reg, + ); + iface.add_method(MethodSignature::new(reg).add_out(reg.hstring())); // 6 AbsoluteUri + iface.add_method(MethodSignature::new(reg).add_out(reg.hstring())); // 7 DisplayUri + iface.add_method(MethodSignature::new(reg).add_out(reg.hstring())); // 8 Domain + iface.add_method(MethodSignature::new(reg).add_out(reg.hstring())); // 9 Extension + iface.add_method(MethodSignature::new(reg).add_out(reg.hstring())); // 10 Fragment + iface.add_method(MethodSignature::new(reg).add_out(reg.hstring())); // 11 Host + iface.add_method(MethodSignature::new(reg).add_out(reg.hstring())); // 12 Password + iface.add_method(MethodSignature::new(reg).add_out(reg.hstring())); // 13 Path + iface.add_method(MethodSignature::new(reg).add_out(reg.hstring())); // 14 Query + iface.add_method(MethodSignature::new(reg).add_out(reg.object())); // 15 QueryParsed + iface.add_method(MethodSignature::new(reg).add_out(reg.hstring())); // 16 RawUri + iface.add_method(MethodSignature::new(reg).add_out(reg.hstring())); // 17 SchemeName + iface.add_method(MethodSignature::new(reg).add_out(reg.hstring())); // 18 UserName + iface.add_method(MethodSignature::new(reg).add_out(reg.i32_type())); // 19 Port + iface.add_method(MethodSignature::new(reg)); // 20 Suspicious (unused) + iface +} + +fn create_uri_dynamic( + reg: &std::sync::Arc, + raw: &str, +) -> windows_core::Result { + let factory = WinRTValue::from_activation_factory(&HSTRING::from("Windows.Foundation.Uri")) + .expect("Windows.Foundation.Uri activation factory"); + let uri_factory = factory + .cast(&IUriRuntimeClassFactory::IID) + .expect("IUriRuntimeClassFactory"); + let mut iface = InterfaceSignature::define_from_iinspectable( + "IUriRuntimeClassFactory", + IUriRuntimeClassFactory::IID, + reg, + ); + iface.add_method( + MethodSignature::new(reg) + .add_in(reg.hstring()) + .add_out(reg.object()), + ); + let uri_factory_obj = uri_factory.as_object().expect("factory object"); + let result = iface.methods[6].call_dynamic( + uri_factory_obj.as_raw(), + &[WinRTValue::HString(HSTRING::from(raw))], + )?; + Ok(result[0].clone()) +} + +fn property_value_statics_signature(reg: &std::sync::Arc) -> InterfaceSignature { + let statics_iid = GUID::from_u128(0x629BDBC8_D932_4FF4_96B9_8D96C5C1E858); + let mut iface = + InterfaceSignature::define_from_iinspectable("IPropertyValueStatics", statics_iid, reg); + for _ in 0..4 { + iface.add_method(MethodSignature::new(reg)); // 6 CreateEmpty through 9 CreateUInt16 + } + iface.add_method( + MethodSignature::new(reg) + .add_in(reg.i32_type()) + .add_out(reg.object()), + ); // 10 CreateInt32 + for _ in 0..6 { + iface.add_method(MethodSignature::new(reg)); // 11 CreateUInt32 through 16 CreateChar16 + } + iface.add_method( + MethodSignature::new(reg) + .add_in(reg.bool_type()) + .add_out(reg.object()), + ); // 17 CreateBoolean + iface.add_method( + MethodSignature::new(reg) + .add_in(reg.hstring()) + .add_out(reg.object()), + ); // 18 CreateString + iface +} + +fn property_value_signature(reg: &std::sync::Arc) -> InterfaceSignature { + let ipv_iid = GUID::from_u128(0x4BD682DD_7554_40E9_9A9B_82654EDE7E62); + let mut iface = InterfaceSignature::define_from_iinspectable("IPropertyValue", ipv_iid, reg); + iface.add_method(MethodSignature::new(reg).add_out(reg.i32_type())); // 6 get_Type + iface.add_method(MethodSignature::new(reg).add_out(reg.bool_type())); // 7 get_IsNumericScalar + for _ in 0..3 { + iface.add_method(MethodSignature::new(reg)); // 8 GetUInt8 through 10 GetUInt16 + } + iface.add_method(MethodSignature::new(reg).add_out(reg.i32_type())); // 11 GetInt32 + for _ in 0..6 { + iface.add_method(MethodSignature::new(reg)); // 12 GetUInt32 through 17 GetChar16 + } + iface.add_method(MethodSignature::new(reg).add_out(reg.bool_type())); // 18 GetBoolean + iface.add_method(MethodSignature::new(reg).add_out(reg.hstring())); // 19 GetString + iface +} + +fn create_property_value( + statics: &WinRTValue, + iface: &InterfaceSignature, + vtable_index: usize, + arg: WinRTValue, +) -> windows_core::Result { + let statics_obj = statics.as_object().expect("statics object"); + Ok(iface.methods[vtable_index].call_dynamic(statics_obj.as_raw(), &[arg])?[0].clone()) +} + +fn as_property_value(value: &WinRTValue) -> WinRTValue { + value + .cast(&IPropertyValue::IID) + .expect("IPropertyValue interface") +} + +fn check_winrt_uri_factory_dynamic_properties_are_golden() -> windows_core::Result<()> { + let reg = MetadataTable::new(); + let uri = create_uri_dynamic(®, "https://www.example.com/a/b?q=2#frag")?; + let uri_obj = uri.as_object().expect("uri object"); + let iface = uri_runtime_class_signature(®); + + assert_hstring( + &iface.methods[6].call_dynamic(uri_obj.as_raw(), &[])?[0], + "https://www.example.com/a/b?q=2#frag", + ); + assert_hstring( + &iface.methods[8].call_dynamic(uri_obj.as_raw(), &[])?[0], + "example.com", + ); + assert_hstring( + &iface.methods[10].call_dynamic(uri_obj.as_raw(), &[])?[0], + "#frag", + ); + assert_hstring( + &iface.methods[11].call_dynamic(uri_obj.as_raw(), &[])?[0], + "www.example.com", + ); + assert_hstring( + &iface.methods[13].call_dynamic(uri_obj.as_raw(), &[])?[0], + "/a/b", + ); + assert_hstring( + &iface.methods[14].call_dynamic(uri_obj.as_raw(), &[])?[0], + "?q=2", + ); + assert_hstring( + &iface.methods[16].call_dynamic(uri_obj.as_raw(), &[])?[0], + "https://www.example.com/a/b?q=2#frag", + ); + assert_hstring( + &iface.methods[17].call_dynamic(uri_obj.as_raw(), &[])?[0], + "https", + ); + assert_eq!( + iface.methods[19].call_dynamic(uri_obj.as_raw(), &[])?[0] + .as_i32() + .unwrap(), + 443 + ); + + Ok(()) +} + +fn check_winrt_uri_empty_path_is_golden() -> windows_core::Result<()> { + let reg = MetadataTable::new(); + let uri = create_uri_dynamic(®, "https://www.example.com")?; + let uri_obj = uri.as_object().expect("uri object"); + let iface = uri_runtime_class_signature(®); + + assert_hstring( + &iface.methods[6].call_dynamic(uri_obj.as_raw(), &[])?[0], + "https://www.example.com/", + ); + assert_hstring( + &iface.methods[13].call_dynamic(uri_obj.as_raw(), &[])?[0], + "/", + ); + assert_hstring( + &iface.methods[14].call_dynamic(uri_obj.as_raw(), &[])?[0], + "", + ); + assert_hstring( + &iface.methods[17].call_dynamic(uri_obj.as_raw(), &[])?[0], + "https", + ); + assert_hstring( + &iface.methods[18].call_dynamic(uri_obj.as_raw(), &[])?[0], + "", + ); + assert_eq!( + iface.methods[19].call_dynamic(uri_obj.as_raw(), &[])?[0] + .as_i32() + .unwrap(), + 443 + ); + + Ok(()) +} + +fn check_property_value_dynamic_scalar_round_trips_are_golden() -> windows_core::Result<()> { + let reg = MetadataTable::new(); + let statics = + WinRTValue::from_activation_factory(&HSTRING::from("Windows.Foundation.PropertyValue")) + .expect("PropertyValue activation factory") + .cast(&GUID::from_u128(0x629BDBC8_D932_4FF4_96B9_8D96C5C1E858)) + .expect("IPropertyValueStatics"); + let statics_iface = property_value_statics_signature(®); + let value_iface = property_value_signature(®); + + let int_value = as_property_value(&create_property_value( + &statics, + &statics_iface, + 10, + WinRTValue::I32(-12345), + )?); + let int_obj = int_value.as_object().expect("IPropertyValue int object"); + assert_eq!( + value_iface.methods[6].call_dynamic(int_obj.as_raw(), &[])?[0] + .as_i32() + .unwrap(), + 4 + ); + assert_bool( + &value_iface.methods[7].call_dynamic(int_obj.as_raw(), &[])?[0], + false, + ); + assert_eq!( + value_iface.methods[11].call_dynamic(int_obj.as_raw(), &[])?[0] + .as_i32() + .unwrap(), + -12345 + ); + + let bool_value = as_property_value(&create_property_value( + &statics, + &statics_iface, + 17, + WinRTValue::Bool(true), + )?); + let bool_obj = bool_value.as_object().expect("IPropertyValue bool object"); + assert_eq!( + value_iface.methods[6].call_dynamic(bool_obj.as_raw(), &[])?[0] + .as_i32() + .unwrap(), + 11 + ); + assert_bool( + &value_iface.methods[7].call_dynamic(bool_obj.as_raw(), &[])?[0], + false, + ); + assert_bool( + &value_iface.methods[18].call_dynamic(bool_obj.as_raw(), &[])?[0], + true, + ); + + let string_value = as_property_value(&create_property_value( + &statics, + &statics_iface, + 18, + WinRTValue::HString(HSTRING::from("dynwinrt regression")), + )?); + let string_obj = string_value + .as_object() + .expect("IPropertyValue string object"); + assert_eq!( + value_iface.methods[6].call_dynamic(string_obj.as_raw(), &[])?[0] + .as_i32() + .unwrap(), + 12 + ); + assert_bool( + &value_iface.methods[7].call_dynamic(string_obj.as_raw(), &[])?[0], + false, + ); + assert_hstring( + &value_iface.methods[19].call_dynamic(string_obj.as_raw(), &[])?[0], + "dynwinrt regression", + ); + + Ok(()) +} + +fn check_property_value_dynamic_type_mismatch_returns_golden_error() -> windows_core::Result<()> { + let reg = MetadataTable::new(); + let statics = + WinRTValue::from_activation_factory(&HSTRING::from("Windows.Foundation.PropertyValue")) + .expect("PropertyValue activation factory") + .cast(&GUID::from_u128(0x629BDBC8_D932_4FF4_96B9_8D96C5C1E858)) + .expect("IPropertyValueStatics"); + let statics_iface = property_value_statics_signature(®); + let value_iface = property_value_signature(®); + let int_value = as_property_value(&create_property_value( + &statics, + &statics_iface, + 10, + WinRTValue::I32(7), + )?); + let int_obj = int_value.as_object().expect("IPropertyValue int object"); + + let err = value_iface.methods[19] + .call_dynamic(int_obj.as_raw(), &[]) + .expect_err("GetString on an Int32 PropertyValue should fail"); + assert_eq!(err.code(), HRESULT(0x80028CA0u32 as i32)); + + Ok(()) +} + +fn check_geopoint_struct_layout_and_dynamic_position_round_trip_are_golden() +-> windows_core::Result<()> { + let reg = MetadataTable::new(); + let f64_type = reg.f64_type(); + let geo_type = reg.struct_type( + "Windows.Devices.Geolocation.BasicGeoposition", + &[f64_type.clone(), f64_type.clone(), f64_type], + ); + assert_eq!(geo_type.size_of(), 24); + assert_eq!(geo_type.align_of(), 8); + assert_eq!(geo_type.field_offset(0), 0); + assert_eq!(geo_type.field_offset(1), 8); + assert_eq!(geo_type.field_offset(2), 16); + + let mut geo_value = geo_type.default_value(); + assert_eq!(geo_value.get_field::(0), 0.0); + assert_eq!(geo_value.get_field::(1), 0.0); + assert_eq!(geo_value.get_field::(2), 0.0); + geo_value.set_field(0, 47.643); + geo_value.set_field(1, -122.131); + geo_value.set_field(2, 100.5); + + let projected = Geopoint::Create(BasicGeoposition { + Latitude: 47.643, + Longitude: -122.131, + Altitude: 100.5, + })?; + let projected_position = projected.Position()?; + assert!((projected_position.Latitude - 47.643).abs() < 1e-6); + assert!((projected_position.Longitude + 122.131).abs() < 1e-6); + assert!((projected_position.Altitude - 100.5).abs() < 1e-6); + + let factory = + WinRTValue::from_activation_factory(&HSTRING::from("Windows.Devices.Geolocation.Geopoint")) + .expect("Geopoint activation factory") + .cast(&IGeopointFactory::IID) + .expect("IGeopointFactory"); + let mut factory_iface = InterfaceSignature::define_from_iinspectable( + "IGeopointFactory", + IGeopointFactory::IID, + ®, + ); + factory_iface.add_method( + MethodSignature::new(®) + .add_in(geo_type.clone()) + .add_out(reg.object()), + ); + let factory_obj = factory.as_object().expect("factory object"); + let created = factory_iface.methods[6] + .call_dynamic(factory_obj.as_raw(), &[WinRTValue::Struct(geo_value)])?; + let geopoint: IGeopoint = created[0].as_object().expect("Geopoint object").cast()?; + + let mut geopoint_iface = + InterfaceSignature::define_from_iinspectable("IGeopoint", IGeopoint::IID, ®); + geopoint_iface.add_method(MethodSignature::new(®).add_out(geo_type)); + let position = geopoint_iface.methods[6].call_dynamic(geopoint.as_raw(), &[])?; + let data = position[0].as_struct().expect("BasicGeoposition struct"); + assert!((data.get_field::(0) - 47.643).abs() < 1e-6); + assert!((data.get_field::(1) + 122.131).abs() < 1e-6); + assert!((data.get_field::(2) - 100.5).abs() < 1e-6); + + Ok(()) +} + +#[test] +fn winrt_regression_harness_golden_behaviors() -> windows_core::Result<()> { + init_winrt(); + + check_winrt_uri_factory_dynamic_properties_are_golden()?; + check_winrt_uri_empty_path_is_golden()?; + check_property_value_dynamic_scalar_round_trips_are_golden()?; + check_property_value_dynamic_type_mismatch_returns_golden_error()?; + check_geopoint_struct_layout_and_dynamic_position_round_trip_are_golden()?; + + Ok(()) +} From 271271c19da43632a4b99f41b70c048001eee114 Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Tue, 21 Jul 2026 10:14:13 +0800 Subject: [PATCH 2/7] Use IPropertyValue IID constant in regression test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- crates/dynwinrt/tests/winrt_regression.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/dynwinrt/tests/winrt_regression.rs b/crates/dynwinrt/tests/winrt_regression.rs index da1e74d5..1203f397 100644 --- a/crates/dynwinrt/tests/winrt_regression.rs +++ b/crates/dynwinrt/tests/winrt_regression.rs @@ -104,8 +104,8 @@ fn property_value_statics_signature(reg: &std::sync::Arc) -> Inte } fn property_value_signature(reg: &std::sync::Arc) -> InterfaceSignature { - let ipv_iid = GUID::from_u128(0x4BD682DD_7554_40E9_9A9B_82654EDE7E62); - let mut iface = InterfaceSignature::define_from_iinspectable("IPropertyValue", ipv_iid, reg); + let mut iface = + InterfaceSignature::define_from_iinspectable("IPropertyValue", IPropertyValue::IID, reg); iface.add_method(MethodSignature::new(reg).add_out(reg.i32_type())); // 6 get_Type iface.add_method(MethodSignature::new(reg).add_out(reg.bool_type())); // 7 get_IsNumericScalar for _ in 0..3 { From 313bbcdf3d2af121f9c070a0c1b59410a534c782 Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Thu, 23 Jul 2026 16:24:33 +0800 Subject: [PATCH 3/7] test(e2e): compare all shared projected attributes, not just the first Copilot review (PR #3): projected_values_equal returned after the FIRST shared attribute (name/value/path) matched, so an object exposing more than one of them (e.g. both .name and .value) could be treated as equal when only the first matches, weakening the regression protocol checks. Compare every shared attribute and return False if any differ; keep the scalar-equality fallback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/runners/py_runner.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/runners/py_runner.py b/tests/runners/py_runner.py index 10a7dbc0..dcbefc36 100644 --- a/tests/runners/py_runner.py +++ b/tests/runners/py_runner.py @@ -48,9 +48,14 @@ def literal_arg(val): def projected_values_equal(left, right): if type(left) is not type(right): return False + matched_any = False for attribute in ("name", "value", "path"): if hasattr(left, attribute) and hasattr(right, attribute): - return getattr(left, attribute) == getattr(right, attribute) + if getattr(left, attribute) != getattr(right, attribute): + return False + matched_any = True + if matched_any: + return True if isinstance(left, (str, bytes, int, float, bool, tuple)): return left == right return True From 3450f8ff498a39d3893e406aaf2206faf8ca1069 Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Thu, 23 Jul 2026 16:51:31 +0800 Subject: [PATCH 4/7] test(e2e): ts_runner handles 'constructor' kind and fails loud on unknown kinds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copilot review: the E2E spec schema includes instantiate.kind 'constructor' (py_runner already handles it), but ts_runner only handled 'activate'/'static_factory', leaving obj null and proceeding for 'constructor' or any unexpected kind — which could report a pass with no object. Add the 'constructor' branch (new cls(...args), mirroring py_runner) and throw on unknown kinds. (The current constructor spec is py-only, so no ts behavior changes today; this is defensive.) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/runners/ts_runner.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/runners/ts_runner.ts b/tests/runners/ts_runner.ts index 6270e5d6..42f95a27 100644 --- a/tests/runners/ts_runner.ts +++ b/tests/runners/ts_runner.ts @@ -121,6 +121,11 @@ async function runSpec( const methodName = toCamelCase(spec.instantiate.method!); const args = spec.instantiate.args || []; obj = cls[methodName](...args); + } else if (instKind === 'constructor') { + const args = spec.instantiate.args || []; + obj = new cls(...args); + } else if (instKind !== 'none') { + throw new Error(`Unknown instantiate.kind '${instKind}' for ${spec.class}`); } // kind === 'none': no instantiation From 1afe21025a3dee0b40aec0a3a78eb4284fdfab87 Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Thu, 23 Jul 2026 17:10:41 +0800 Subject: [PATCH 5/7] test(regression): gate on cfg(windows) and only tolerate RPC_E_CHANGED_MODE in init Copilot review (PR #3): - winrt_regression.rs uses WinRT/Win32 APIs (RoInitialize etc.) and won't compile on non-Windows; add #![cfg(windows)] so cargo test stays usable off-Windows. - init_winrt ignored ALL RoInitialize failures despite the comment saying only the already-initialized case is benign. Now it accepts only RPC_E_CHANGED_MODE (already initialized in a different apartment; S_FALSE is already Ok) and panics on any other HRESULT so a genuine init failure is diagnosable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- crates/dynwinrt/tests/winrt_regression.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/crates/dynwinrt/tests/winrt_regression.rs b/crates/dynwinrt/tests/winrt_regression.rs index 1203f397..9c2a25dd 100644 --- a/crates/dynwinrt/tests/winrt_regression.rs +++ b/crates/dynwinrt/tests/winrt_regression.rs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#![cfg(windows)] use dynwinrt::{InterfaceSignature, MetadataTable, MethodSignature, WinRTValue}; use windows::Devices::Geolocation::{BasicGeoposition, Geopoint, IGeopoint, IGeopointFactory}; @@ -8,9 +9,19 @@ use windows::Win32::System::WinRT::{RO_INIT_MULTITHREADED, RoInitialize}; use windows_core::{GUID, HRESULT, HSTRING, Interface}; fn init_winrt() { - // The process may already be initialized by another test; both success and - // already-initialized failures are acceptable for these headless WinRT calls. - let _ = unsafe { RoInitialize(RO_INIT_MULTITHREADED) }; + // The process may already be initialized by another test. `RoInitialize` + // returns `Ok` for S_FALSE (already initialized in the same apartment), and + // `RPC_E_CHANGED_MODE` when it is already initialized in a different + // apartment — both are acceptable for these headless WinRT calls. Any other + // failure is a genuine problem and must not be silently ignored. + const RPC_E_CHANGED_MODE: HRESULT = HRESULT(0x8001_0106u32 as i32); + if let Err(e) = unsafe { RoInitialize(RO_INIT_MULTITHREADED) } { + assert_eq!( + e.code(), + RPC_E_CHANGED_MODE, + "RoInitialize failed unexpectedly (only RPC_E_CHANGED_MODE is benign): {e:?}" + ); + } } fn assert_hstring(value: &WinRTValue, expected: &str) { From 9421935701ad316f78d26d5bc3b02ed535909d99 Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Thu, 23 Jul 2026 17:32:35 +0800 Subject: [PATCH 6/7] test(regression): name the TYPE_E_TYPEMISMATCH HRESULT instead of a raw literal Copilot review (PR #3): the golden-behavior harness asserted against a raw 0x80028CA0 HRESULT literal. Name it TYPE_E_TYPEMISMATCH locally (GetString on a non-string IPropertyValue) for readability/maintainability. --- crates/dynwinrt/tests/winrt_regression.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/dynwinrt/tests/winrt_regression.rs b/crates/dynwinrt/tests/winrt_regression.rs index 9c2a25dd..e7f0706e 100644 --- a/crates/dynwinrt/tests/winrt_regression.rs +++ b/crates/dynwinrt/tests/winrt_regression.rs @@ -334,7 +334,9 @@ fn check_property_value_dynamic_type_mismatch_returns_golden_error() -> windows_ let err = value_iface.methods[19] .call_dynamic(int_obj.as_raw(), &[]) .expect_err("GetString on an Int32 PropertyValue should fail"); - assert_eq!(err.code(), HRESULT(0x80028CA0u32 as i32)); + // GetString on a non-string IPropertyValue fails with TYPE_E_TYPEMISMATCH. + const TYPE_E_TYPEMISMATCH: HRESULT = HRESULT(0x8002_8CA0u32 as i32); + assert_eq!(err.code(), TYPE_E_TYPEMISMATCH); Ok(()) } From 87609922a30d6e6932c36afa7822183b0cbd9c8b Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Thu, 23 Jul 2026 17:48:24 +0800 Subject: [PATCH 7/7] test(e2e): add 'constructor' to Instantiate.kind union (fixes TS2367) The earlier ts_runner 'constructor' handling compared instKind against a literal not in the Instantiate.kind union ('activate' | 'static_factory' | 'none'), which a type-checked (tsc) run rejects as TS2367 (no overlap). Extend the union to include 'constructor'. --- tests/runners/ts_runner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/runners/ts_runner.ts b/tests/runners/ts_runner.ts index 42f95a27..938047f9 100644 --- a/tests/runners/ts_runner.ts +++ b/tests/runners/ts_runner.ts @@ -16,7 +16,7 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; interface Instantiate { - kind: 'activate' | 'static_factory' | 'none'; + kind: 'activate' | 'static_factory' | 'constructor' | 'none'; method?: string; args?: any[]; }