Skip to content

Commit ed5fe41

Browse files
committed
Added tests for the 'pythoncom.com_record' subclassing functionality.
1 parent c9aeec2 commit ed5fe41

File tree

1 file changed

+124
-25
lines changed

1 file changed

+124
-25
lines changed

com/win32com/test/testPyComTest.py

Lines changed: 124 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@
1717
import win32com.test.util
1818
import win32timezone
1919
import winerror
20-
from win32com.client import VARIANT, CastTo, DispatchBaseClass, constants
20+
from win32com.client import (
21+
VARIANT,
22+
CastTo,
23+
DispatchBaseClass,
24+
constants,
25+
Record,
26+
register_record_class,
27+
)
2128

2229
importMsg = "**** PyCOMTest is not installed ***\n PyCOMTest is a Python test specific COM client and server.\n It is likely this server is not installed on this machine\n To install the server, you must get the win32com sources\n and build it using MS Visual C++"
2330

@@ -45,6 +52,46 @@
4552
verbose = 0
4653

4754

55+
# Subclasses of pythoncom.com_record.
56+
# Registration is performed in 'TestGenerated'.
57+
class TestStruct1(pythoncom.com_record):
58+
__slots__ = tuple()
59+
TLBID = "{6BCDCB60-5605-11D0-AE5F-CADD4C000000}"
60+
MJVER = 1
61+
MNVER = 1
62+
LCID = 0
63+
GUID = "{7A4CE6A7-7959-4E85-A3C0-B41442FF0F67}"
64+
65+
66+
class TestStruct2(pythoncom.com_record):
67+
__slots__ = tuple()
68+
TLBID = "{6BCDCB60-5605-11D0-AE5F-CADD4C000000}"
69+
MJVER = 1
70+
MNVER = 1
71+
LCID = 0
72+
GUID = "{78F0EA07-B7CF-42EA-A251-A4C6269F76AF}"
73+
74+
75+
# We don't need to stick with the struct name in the TypeLibrry for the subclass name.
76+
# The following class has the same GUID as TestStruct2 from the TypeLibrary.
77+
class ArrayOfStructsTestStruct(pythoncom.com_record):
78+
__slots__ = tuple()
79+
TLBID = "{6BCDCB60-5605-11D0-AE5F-CADD4C000000}"
80+
MJVER = 1
81+
MNVER = 1
82+
LCID = 0
83+
GUID = "{78F0EA07-B7CF-42EA-A251-A4C6269F76AF}"
84+
85+
86+
class NotInTypeLibraryTestStruct(pythoncom.com_record):
87+
__slots__ = tuple()
88+
TLBID = "{6BCDCB60-5605-11D0-AE5F-CADD4C000000}"
89+
MJVER = 1
90+
MNVER = 1
91+
LCID = 0
92+
GUID = "{79BB6AC3-12DE-4AC5-88AC-225C29A58043}"
93+
94+
4895
def check_get_set(func, arg):
4996
got = func(arg)
5097
assert got == arg, f"{func} failed - expected {arg!r}, got {got!r}"
@@ -198,18 +245,6 @@ def TestCommon(o, is_generated):
198245
progress("Checking structs")
199246
r = o.GetStruct()
200247
assert r.int_value == 99 and str(r.str_value) == "Hello from C++"
201-
# Dynamic does not support struct byref as [ in, out ] parameters
202-
if hasattr(o, "CLSID"):
203-
progress("Checking struct byref as [ in, out ] parameter")
204-
mod_r = o.ModifyStruct(r)
205-
# We expect the input value to stay unchanged
206-
assert r.int_value == 99 and str(r.str_value) == "Hello from C++"
207-
# and the return value to reflect the modifications performed on the COM server side
208-
assert (
209-
mod_r.int_value == 100
210-
and str(mod_r.str_value) == "Nothing is as constant as change"
211-
)
212-
213248
assert o.DoubleString("foo") == "foofoo"
214249

215250
progress("Checking var args")
@@ -409,9 +444,46 @@ def TestDynamic():
409444
# assert o.ParamProp(0) == 1, o.paramProp(0)
410445

411446

447+
def TestStructByref(o, r):
448+
progress("Checking struct byref as [ in, out ] parameter")
449+
mod_r = o.ModifyStruct(r)
450+
# If 'TestStruct1' was registered as an instantiable subclass
451+
# of pythoncom.com_record, the return value should have this type.
452+
if isinstance(r, TestStruct1):
453+
assert type(mod_r) is TestStruct1
454+
else:
455+
assert type(mod_r) is pythoncom.com_record
456+
# We expect the input value to stay unchanged
457+
assert r.int_value == 99 and str(r.str_value) == "Hello from C++"
458+
# and the return value to reflect the modifications performed on the COM server side
459+
assert (
460+
mod_r.int_value == 100
461+
and str(mod_r.str_value) == "Nothing is as constant as change"
462+
)
463+
464+
465+
def TestArrayOfStructs(o, test_rec):
466+
progress("Testing struct with SAFEARRAY(VT_RECORD) fields.")
467+
rec_list = []
468+
for i in range(3):
469+
# If 'ArrayOfStructsTestStruct' and 'TestStruct1' were registered as instantiable
470+
# subclasses of pythoncom.com_record, we expect to work with these types.
471+
if isinstance(test_rec, ArrayOfStructsTestStruct):
472+
rec = TestStruct1()
473+
assert type(rec) is TestStruct1
474+
else:
475+
rec = Record("TestStruct1", o)
476+
assert type(rec) is pythoncom.com_record
477+
rec.str_value = "This is record number"
478+
rec.int_value = i + 1
479+
rec_list.append(rec)
480+
test_rec.array_of_records = rec_list
481+
test_rec.rec_count = i + 1
482+
assert o.VerifyArrayOfStructs(test_rec)
483+
484+
412485
def TestGenerated():
413486
# Create an instance of the server.
414-
from win32com.client import Record
415487
from win32com.client.gencache import EnsureDispatch
416488

417489
o = EnsureDispatch("PyCOMTest.PyCOMTest")
@@ -434,18 +506,45 @@ def TestGenerated():
434506
coclass = GetClass("{B88DD310-BAE8-11D0-AE86-76F2C1000000}")()
435507
TestCounter(coclass, True)
436508

437-
# Test records with SAFEARRAY(VT_RECORD) fields.
438-
progress("Testing records with SAFEARRAY(VT_RECORD) fields.")
439-
l = []
440-
for i in range(3):
441-
rec = Record("TestStruct1", o)
442-
rec.str_value = "This is record number"
443-
rec.int_value = i + 1
444-
l.append(rec)
509+
# Test plain pythoncom.com_record structs.
510+
progress("Testing baseclass pythoncom.com_record structs.")
511+
r = o.GetStruct()
512+
assert type(r) is pythoncom.com_record
513+
TestStructByref(o, r)
445514
test_rec = Record("TestStruct2", o)
446-
test_rec.array_of_records = l
447-
test_rec.rec_count = i + 1
448-
assert o.VerifyArrayOfStructs(test_rec)
515+
assert type(test_rec) is pythoncom.com_record
516+
TestArrayOfStructs(o, test_rec)
517+
518+
progress("Testing registration of pythoncom.com_record subclasses.")
519+
# Instantiating a pythoncom.com_record subclass, which has proper GUID attributes,
520+
# does return an instance of the base class, as long as we have not registered it.
521+
r_base = TestStruct1()
522+
assert type(r_base) is pythoncom.com_record
523+
# Register the subclasses in pythoncom.
524+
register_record_class(TestStruct1)
525+
register_record_class(ArrayOfStructsTestStruct)
526+
# Now the type of the instance is the registered subclass.
527+
r_sub = TestStruct1()
528+
assert type(r_sub) is TestStruct1
529+
# Now also the 'Record' factory function returns an instance of the registered subtype.
530+
r_sub = Record("TestStruct1", o)
531+
assert type(r_sub) is TestStruct1
532+
# It should not be possible to register multiple classes with the same GUID, e.g.
533+
# 'TestStruct2' has the same GUID class attribute value as 'ArrayOfStructsTestStruct'.
534+
check_get_set_raises(ValueError, register_record_class, TestStruct2)
535+
# Also registering a class with a GUID that is not in the TypeLibrary should fail.
536+
check_get_set_raises(TypeError, register_record_class, NotInTypeLibraryTestStruct)
537+
538+
# Perform the 'Byref' and 'ArrayOfStruct tests using the registered subclasses.
539+
progress("Testing subclasses of pythoncom.com_record.")
540+
r = o.GetStruct()
541+
# After 'TestStruct1' was registered as an instantiable subclass
542+
# of pythoncom.com_record, the return value should have this type.
543+
assert type(r) is TestStruct1
544+
TestStructByref(o, r)
545+
test_rec = ArrayOfStructsTestStruct()
546+
assert type(test_rec) is ArrayOfStructsTestStruct
547+
TestArrayOfStructs(o, test_rec)
449548

450549
# XXX - this is failing in dynamic tests, but should work fine.
451550
i1, i2 = o.GetMultipleInterfaces()

0 commit comments

Comments
 (0)