123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594 |
- # Test code for a VB Program.
- #
- # This requires the PythonCOM VB Test Harness.
- #
-
- import sys
- import traceback
-
- import pythoncom
- import win32com.client
- import win32com.client.dynamic
- import win32com.client.gencache
- import winerror
- from pywin32_testutil import str2memory
- from win32com.server.util import NewCollection, wrap
- from win32com.test import util
-
- # for debugging
- useDispatcher = None
- ## import win32com.server.dispatcher
- ## useDispatcher = win32com.server.dispatcher.DefaultDebugDispatcher
-
- error = RuntimeError
-
-
- # Set up a COM object that VB will do some callbacks on. This is used
- # to test byref params for gateway IDispatch.
- class TestObject:
- _public_methods_ = [
- "CallbackVoidOneByRef",
- "CallbackResultOneByRef",
- "CallbackVoidTwoByRef",
- "CallbackString",
- "CallbackResultOneByRefButReturnNone",
- "CallbackVoidOneByRefButReturnNone",
- "CallbackArrayResult",
- "CallbackArrayResultOneArrayByRef",
- "CallbackArrayResultWrongSize",
- ]
-
- def CallbackVoidOneByRef(self, intVal):
- return intVal + 1
-
- def CallbackResultOneByRef(self, intVal):
- return intVal, intVal + 1
-
- def CallbackVoidTwoByRef(self, int1, int2):
- return int1 + int2, int1 - int2
-
- def CallbackString(self, strVal):
- return 0, strVal + " has visited Python"
-
- def CallbackArrayResult(self, arrayVal):
- ret = []
- for i in arrayVal:
- ret.append(i + 1)
- # returning as a list forces it be processed as a single result
- # (rather than a tuple, where it may be interpreted as
- # multiple results for byref unpacking)
- return ret
-
- def CallbackArrayResultWrongSize(self, arrayVal):
- return list(arrayVal[:-1])
-
- def CallbackArrayResultOneArrayByRef(self, arrayVal):
- ret = []
- for i in arrayVal:
- ret.append(i + 1)
- # See above for list processing.
- return list(arrayVal), ret
-
- def CallbackResultOneByRefButReturnNone(self, intVal):
- return
-
- def CallbackVoidOneByRefButReturnNone(self, intVal):
- return
-
-
- def TestVB(vbtest, bUseGenerated):
- vbtest.LongProperty = -1
- if vbtest.LongProperty != -1:
- raise error("Could not set the long property correctly.")
- vbtest.IntProperty = 10
- if vbtest.IntProperty != 10:
- raise error("Could not set the integer property correctly.")
- vbtest.VariantProperty = 10
- if vbtest.VariantProperty != 10:
- raise error("Could not set the variant integer property correctly.")
- vbtest.VariantProperty = str2memory("raw\0data")
- if vbtest.VariantProperty != str2memory("raw\0data"):
- raise error("Could not set the variant buffer property correctly.")
- vbtest.StringProperty = "Hello from Python"
- if vbtest.StringProperty != "Hello from Python":
- raise error("Could not set the string property correctly.")
- vbtest.VariantProperty = "Hello from Python"
- if vbtest.VariantProperty != "Hello from Python":
- raise error("Could not set the variant string property correctly.")
- vbtest.VariantProperty = (1.0, 2.0, 3.0)
- if vbtest.VariantProperty != (1.0, 2.0, 3.0):
- raise error(
- "Could not set the variant property to an array of floats correctly - '%s'."
- % (vbtest.VariantProperty,)
- )
-
- TestArrays(vbtest, bUseGenerated)
- TestStructs(vbtest)
- TestCollections(vbtest)
-
- assert vbtest.TakeByValObject(vbtest) == vbtest
-
- # Python doesnt support PUTREF properties without a typeref
- # (although we could)
- if bUseGenerated:
- ob = vbtest.TakeByRefObject(vbtest)
- assert ob[0] == vbtest and ob[1] == vbtest
-
- # A property that only has PUTREF defined.
- vbtest.VariantPutref = vbtest
- if vbtest.VariantPutref._oleobj_ != vbtest._oleobj_:
- raise error("Could not set the VariantPutref property correctly.")
- # Cant test further types for this VariantPutref, as only
- # COM objects can be stored ByRef.
-
- # A "set" type property - only works for generated.
- # VB recognizes a collection via a few "private" interfaces that we
- # could later build support in for.
- # vbtest.CollectionProperty = NewCollection((1,2,"3", "Four"))
- # if vbtest.CollectionProperty != (1,2,"3", "Four"):
- # raise error("Could not set the Collection property correctly - got back " + str(vbtest.CollectionProperty))
-
- # These are sub's that have a single byref param
- # Result should be just the byref.
- if vbtest.IncrementIntegerParam(1) != 2:
- raise error("Could not pass an integer byref")
-
- # Sigh - we cant have *both* "ommited byref" and optional args
- # We really have to opt that args nominated as optional work as optional
- # rather than simply all byrefs working as optional.
- # if vbtest.IncrementIntegerParam() != 1:
- # raise error("Could not pass an omitted integer byref")
-
- if vbtest.IncrementVariantParam(1) != 2:
- raise error(
- "Could not pass an int VARIANT byref:"
- + str(vbtest.IncrementVariantParam(1))
- )
-
- if vbtest.IncrementVariantParam(1.5) != 2.5:
- raise error("Could not pass a float VARIANT byref")
-
- # Can't test IncrementVariantParam with the param omitted as it
- # it not declared in the VB code as "Optional"
- callback_ob = wrap(TestObject(), useDispatcher=useDispatcher)
- vbtest.DoSomeCallbacks(callback_ob)
-
- ret = vbtest.PassIntByVal(1)
- if ret != 2:
- raise error("Could not increment the integer - " + str(ret))
-
- TestVBInterface(vbtest)
- # Python doesnt support byrefs without some sort of generated support.
- if bUseGenerated:
- # This is a VB function that takes a single byref
- # Hence 2 return values - function and byref.
- ret = vbtest.PassIntByRef(1)
- if ret != (1, 2):
- raise error("Could not increment the integer - " + str(ret))
- # Check you can leave a byref arg blank.
-
-
- # see above
- # ret = vbtest.PassIntByRef()
- # if ret != (0,1):
- # raise error("Could not increment the integer with default arg- "+str(ret))
-
-
- def _DoTestCollection(vbtest, col_name, expected):
- # It sucks that some objects allow "Count()", but others "Count"
- def _getcount(ob):
- r = getattr(ob, "Count")
- if type(r) != type(0):
- return r()
- return r
-
- c = getattr(vbtest, col_name)
- check = []
- for item in c:
- check.append(item)
- if check != list(expected):
- raise error(
- "Collection %s didn't have %r (had %r)" % (col_name, expected, check)
- )
- # Just looping over the collection again works (ie, is restartable)
- check = []
- for item in c:
- check.append(item)
- if check != list(expected):
- raise error(
- "Collection 2nd time around %s didn't have %r (had %r)"
- % (col_name, expected, check)
- )
- # Check we can get it via iter()
- i = iter(getattr(vbtest, col_name))
- check = []
- for item in i:
- check.append(item)
- if check != list(expected):
- raise error(
- "Collection iterator %s didn't have %r 2nd time around (had %r)"
- % (col_name, expected, check)
- )
- # but an iterator is not restartable
- check = []
- for item in i:
- check.append(item)
- if check != []:
- raise error(
- "2nd time around Collection iterator %s wasn't empty (had %r)"
- % (col_name, check)
- )
-
- # Check len()==Count()
- c = getattr(vbtest, col_name)
- if len(c) != _getcount(c):
- raise error(
- "Collection %s __len__(%r) wasn't==Count(%r)"
- % (col_name, len(c), _getcount(c))
- )
- # Check we can do it with zero based indexing.
- c = getattr(vbtest, col_name)
- check = []
- for i in range(_getcount(c)):
- check.append(c[i])
- if check != list(expected):
- raise error(
- "Collection %s didn't have %r (had %r)" % (col_name, expected, check)
- )
-
- # Check we can do it with our old "Skip/Next" methods.
- c = getattr(vbtest, col_name)._NewEnum()
- check = []
- while 1:
- n = c.Next()
- if not n:
- break
- check.append(n[0])
- if check != list(expected):
- raise error(
- "Collection %s didn't have %r (had %r)" % (col_name, expected, check)
- )
-
-
- def TestCollections(vbtest):
- _DoTestCollection(vbtest, "CollectionProperty", [1, "Two", "3"])
- # zero based indexing works for simple VB collections.
- if vbtest.CollectionProperty[0] != 1:
- raise error("The CollectionProperty[0] element was not the default value")
-
- _DoTestCollection(vbtest, "EnumerableCollectionProperty", [])
- vbtest.EnumerableCollectionProperty.Add(1)
- vbtest.EnumerableCollectionProperty.Add("Two")
- vbtest.EnumerableCollectionProperty.Add("3")
- _DoTestCollection(vbtest, "EnumerableCollectionProperty", [1, "Two", "3"])
-
-
- def _DoTestArray(vbtest, data, expected_exception=None):
- try:
- vbtest.ArrayProperty = data
- if expected_exception is not None:
- raise error("Expected '%s'" % expected_exception)
- except expected_exception:
- return
- got = vbtest.ArrayProperty
- if got != data:
- raise error(
- "Could not set the array data correctly - got %r, expected %r" % (got, data)
- )
-
-
- def TestArrays(vbtest, bUseGenerated):
- # Try and use a safe array (note that the VB code has this declared as a VARIANT
- # and I cant work out how to force it to use native arrays!
- # (NOTE Python will convert incoming arrays to tuples, so we pass a tuple, even tho
- # a list works fine - just makes it easier for us to compare the result!
- # Empty array
- _DoTestArray(vbtest, ())
- # Empty child array
- _DoTestArray(vbtest, ((), ()))
- # ints
- _DoTestArray(vbtest, tuple(range(1, 100)))
- # Floats
- _DoTestArray(vbtest, (1.0, 2.0, 3.0))
- # Strings.
- _DoTestArray(vbtest, tuple("Hello from Python".split()))
- # Date and Time?
- # COM objects.
- _DoTestArray(vbtest, (vbtest, vbtest))
- # Mixed
- _DoTestArray(vbtest, (1, 2.0, "3"))
- # Array alements containing other arrays
- _DoTestArray(vbtest, (1, (vbtest, vbtest), ("3", "4")))
- # Multi-dimensional
- _DoTestArray(vbtest, (((1, 2, 3), (4, 5, 6))))
- _DoTestArray(vbtest, (((vbtest, vbtest, vbtest), (vbtest, vbtest, vbtest))))
- # Another dimension!
- arrayData = (((1, 2), (3, 4), (5, 6)), ((7, 8), (9, 10), (11, 12)))
- arrayData = (
- ((vbtest, vbtest), (vbtest, vbtest), (vbtest, vbtest)),
- ((vbtest, vbtest), (vbtest, vbtest), (vbtest, vbtest)),
- )
- _DoTestArray(vbtest, arrayData)
-
- # Check that when a '__getitem__ that fails' object is the first item
- # in the structure, we don't mistake it for a sequence.
- _DoTestArray(vbtest, (vbtest, 2.0, "3"))
- _DoTestArray(vbtest, (1, 2.0, vbtest))
-
- # Pass arbitrarily sized arrays - these used to fail, but thanks to
- # Stefan Schukat, they now work!
- expected_exception = None
- arrayData = (((1, 2, 1), (3, 4), (5, 6)), ((7, 8), (9, 10), (11, 12)))
- _DoTestArray(vbtest, arrayData, expected_exception)
- arrayData = (((vbtest, vbtest),), ((vbtest,),))
- _DoTestArray(vbtest, arrayData, expected_exception)
- # Pass bad data - last item wrong size
- arrayData = (((1, 2), (3, 4), (5, 6, 8)), ((7, 8), (9, 10), (11, 12)))
- _DoTestArray(vbtest, arrayData, expected_exception)
-
- # byref safearray results with incorrect size.
- callback_ob = wrap(TestObject(), useDispatcher=useDispatcher)
- print("** Expecting a 'ValueError' exception to be printed next:")
- try:
- vbtest.DoCallbackSafeArraySizeFail(callback_ob)
- except pythoncom.com_error as exc:
- assert (
- exc.excepinfo[1] == "Python COM Server Internal Error"
- ), "Didnt get the correct exception - '%s'" % (exc,)
-
- if bUseGenerated:
- # This one is a bit strange! The array param is "ByRef", as VB insists.
- # The function itself also _returns_ the arram param.
- # Therefore, Python sees _2_ result values - one for the result,
- # and one for the byref.
- testData = "Mark was here".split()
- resultData, byRefParam = vbtest.PassSAFEARRAY(testData)
- if testData != list(resultData):
- raise error(
- "The safe array data was not what we expected - got " + str(resultData)
- )
- if testData != list(byRefParam):
- raise error(
- "The safe array data was not what we expected - got " + str(byRefParam)
- )
- testData = [1.0, 2.0, 3.0]
- resultData, byRefParam = vbtest.PassSAFEARRAYVariant(testData)
- assert testData == list(byRefParam)
- assert testData == list(resultData)
- testData = ["hi", "from", "Python"]
- resultData, byRefParam = vbtest.PassSAFEARRAYVariant(testData)
- assert testData == list(byRefParam), "Expected '%s', got '%s'" % (
- testData,
- list(byRefParam),
- )
- assert testData == list(resultData), "Expected '%s', got '%s'" % (
- testData,
- list(resultData),
- )
- # This time, instead of an explicit str() for 1.5, we just
- # pass Unicode, so the result should compare equal
- testData = [1, 2.0, "3"]
- resultData, byRefParam = vbtest.PassSAFEARRAYVariant(testData)
- assert testData == list(byRefParam)
- assert testData == list(resultData)
- print("Array tests passed")
-
-
- def TestStructs(vbtest):
- try:
- vbtest.IntProperty = "One"
- raise error("Should have failed by now")
- except pythoncom.com_error as exc:
- if exc.hresult != winerror.DISP_E_TYPEMISMATCH:
- raise error("Expected DISP_E_TYPEMISMATCH")
-
- s = vbtest.StructProperty
- if s.int_val != 99 or str(s.str_val) != "hello":
- raise error("The struct value was not correct")
- s.str_val = "Hi from Python"
- s.int_val = 11
- if s.int_val != 11 or str(s.str_val) != "Hi from Python":
- raise error("The struct value didnt persist!")
-
- if s.sub_val.int_val != 66 or str(s.sub_val.str_val) != "sub hello":
- raise error("The sub-struct value was not correct")
- sub = s.sub_val
- sub.int_val = 22
- if sub.int_val != 22:
- print(sub.int_val)
- raise error("The sub-struct value didnt persist!")
-
- if s.sub_val.int_val != 22:
- print(s.sub_val.int_val)
- raise error("The sub-struct value (re-fetched) didnt persist!")
-
- if (
- s.sub_val.array_val[0].int_val != 0
- or str(s.sub_val.array_val[0].str_val) != "zero"
- ):
- print(s.sub_val.array_val[0].int_val)
- raise error("The array element wasnt correct")
- s.sub_val.array_val[0].int_val = 99
- s.sub_val.array_val[1].int_val = 66
- if s.sub_val.array_val[0].int_val != 99 or s.sub_val.array_val[1].int_val != 66:
- print(s.sub_val.array_val[0].int_val)
- raise error("The array element didnt persist.")
- # Now pass the struct back to VB
- vbtest.StructProperty = s
- # And get it back again
- s = vbtest.StructProperty
- if s.int_val != 11 or str(s.str_val) != "Hi from Python":
- raise error("After sending to VB, the struct value didnt persist!")
- if s.sub_val.array_val[0].int_val != 99:
- raise error("After sending to VB, the struct array value didnt persist!")
-
- # Now do some object equality tests.
- assert s == s
- assert s != None
- if sys.version_info > (3, 0):
- try:
- s < None
- raise error("Expected type error")
- except TypeError:
- pass
- try:
- None < s
- raise error("Expected type error")
- except TypeError:
- pass
- assert s != s.sub_val
- import copy
-
- s2 = copy.copy(s)
- assert s is not s2
- assert s == s2
- s2.int_val = 123
- assert s != s2
- # Make sure everything works with functions
- s2 = vbtest.GetStructFunc()
- assert s == s2
- vbtest.SetStructSub(s2)
-
- # Create a new structure, and set its elements.
- s = win32com.client.Record("VBStruct", vbtest)
- assert s.int_val == 0, "new struct inst initialized correctly!"
- s.int_val = -1
- vbtest.SetStructSub(s)
- assert vbtest.GetStructFunc().int_val == -1, "new struct didnt make the round trip!"
- # Finally, test stand-alone structure arrays.
- s_array = vbtest.StructArrayProperty
- assert s_array is None, "Expected None from the uninitialized VB array"
- vbtest.MakeStructArrayProperty(3)
- s_array = vbtest.StructArrayProperty
- assert len(s_array) == 3
- for i in range(len(s_array)):
- assert s_array[i].int_val == i
- assert s_array[i].sub_val.int_val == i
- assert s_array[i].sub_val.array_val[0].int_val == i
- assert s_array[i].sub_val.array_val[1].int_val == i + 1
- assert s_array[i].sub_val.array_val[2].int_val == i + 2
-
- # Some error type checks.
- try:
- s.bad_attribute
- raise RuntimeError("Could get a bad attribute")
- except AttributeError:
- pass
- m = s.__members__
- assert (
- m[0] == "int_val"
- and m[1] == "str_val"
- and m[2] == "ob_val"
- and m[3] == "sub_val"
- ), m
-
- # Test attribute errors.
- try:
- s.foo
- raise RuntimeError("Expected attribute error")
- except AttributeError as exc:
- assert "foo" in str(exc), exc
-
- # test repr - it uses repr() of the sub-objects, so check it matches.
- expected = "com_struct(int_val=%r, str_val=%r, ob_val=%r, sub_val=%r)" % (
- s.int_val,
- s.str_val,
- s.ob_val,
- s.sub_val,
- )
- if repr(s) != expected:
- print("Expected repr:", expected)
- print("Actual repr :", repr(s))
- raise RuntimeError("repr() of record object failed")
-
- print("Struct/Record tests passed")
-
-
- def TestVBInterface(ob):
- t = ob.GetInterfaceTester(2)
- if t.getn() != 2:
- raise error("Initial value wrong")
- t.setn(3)
- if t.getn() != 3:
- raise error("New value wrong")
-
-
- def TestObjectSemantics(ob):
- # a convenient place to test some of our equality semantics
- assert ob == ob._oleobj_
- assert not ob != ob._oleobj_
- # same test again, but lhs and rhs reversed.
- assert ob._oleobj_ == ob
- assert not ob._oleobj_ != ob
- # same tests but against different pointers. COM identity rules should
- # still ensure all works
- assert ob._oleobj_ == ob._oleobj_.QueryInterface(pythoncom.IID_IUnknown)
- assert not ob._oleobj_ != ob._oleobj_.QueryInterface(pythoncom.IID_IUnknown)
-
- assert ob._oleobj_ != None
- assert None != ob._oleobj_
- assert ob != None
- assert None != ob
- if sys.version_info > (3, 0):
- try:
- ob < None
- raise error("Expected type error")
- except TypeError:
- pass
- try:
- None < ob
- raise error("Expected type error")
- except TypeError:
- pass
-
- assert ob._oleobj_.QueryInterface(pythoncom.IID_IUnknown) == ob._oleobj_
- assert not ob._oleobj_.QueryInterface(pythoncom.IID_IUnknown) != ob._oleobj_
-
- assert ob._oleobj_ == ob._oleobj_.QueryInterface(pythoncom.IID_IDispatch)
- assert not ob._oleobj_ != ob._oleobj_.QueryInterface(pythoncom.IID_IDispatch)
-
- assert ob._oleobj_.QueryInterface(pythoncom.IID_IDispatch) == ob._oleobj_
- assert not ob._oleobj_.QueryInterface(pythoncom.IID_IDispatch) != ob._oleobj_
-
- print("Object semantic tests passed")
-
-
- def DoTestAll():
- o = win32com.client.Dispatch("PyCOMVBTest.Tester")
- TestObjectSemantics(o)
- TestVB(o, 1)
-
- o = win32com.client.dynamic.DumbDispatch("PyCOMVBTest.Tester")
- TestObjectSemantics(o)
- TestVB(o, 0)
-
-
- def TestAll():
- # Import the type library for the test module. Let the 'invalid clsid'
- # exception filter up, where the test runner will treat it as 'skipped'
- win32com.client.gencache.EnsureDispatch("PyCOMVBTest.Tester")
-
- if not __debug__:
- raise RuntimeError("This must be run in debug mode - we use assert!")
- try:
- DoTestAll()
- print("All tests appear to have worked!")
- except:
- # ?????
- print("TestAll() failed!!")
- traceback.print_exc()
- raise
-
-
- # Make this test run under our test suite to leak tests etc work
- def suite():
- import unittest
-
- test = util.CapturingFunctionTestCase(TestAll, description="VB tests")
- suite = unittest.TestSuite()
- suite.addTest(test)
- return suite
-
-
- if __name__ == "__main__":
- util.testmain()
|