testvb.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. # Test code for a VB Program.
  2. #
  3. # This requires the PythonCOM VB Test Harness.
  4. #
  5. import sys
  6. import winerror
  7. import pythoncom, win32com.client, win32com.client.dynamic, win32com.client.gencache
  8. from win32com.server.util import NewCollection, wrap
  9. from win32com.test import util
  10. from pywin32_testutil import str2memory
  11. import traceback
  12. # for debugging
  13. useDispatcher = None
  14. ## import win32com.server.dispatcher
  15. ## useDispatcher = win32com.server.dispatcher.DefaultDebugDispatcher
  16. error = RuntimeError
  17. # Set up a COM object that VB will do some callbacks on. This is used
  18. # to test byref params for gateway IDispatch.
  19. class TestObject:
  20. _public_methods_ = ["CallbackVoidOneByRef","CallbackResultOneByRef", "CallbackVoidTwoByRef",
  21. "CallbackString","CallbackResultOneByRefButReturnNone",
  22. "CallbackVoidOneByRefButReturnNone",
  23. "CallbackArrayResult", "CallbackArrayResultOneArrayByRef",
  24. "CallbackArrayResultWrongSize"
  25. ]
  26. def CallbackVoidOneByRef(self, intVal):
  27. return intVal + 1
  28. def CallbackResultOneByRef(self, intVal):
  29. return intVal, intVal + 1
  30. def CallbackVoidTwoByRef(self, int1, int2):
  31. return int1+int2, int1-int2
  32. def CallbackString(self, strVal):
  33. return 0, strVal + " has visited Python"
  34. def CallbackArrayResult(self, arrayVal):
  35. ret = []
  36. for i in arrayVal:
  37. ret.append(i+1)
  38. # returning as a list forces it be processed as a single result
  39. # (rather than a tuple, where it may be interpreted as
  40. # multiple results for byref unpacking)
  41. return ret
  42. def CallbackArrayResultWrongSize(self, arrayVal):
  43. return list(arrayVal[:-1])
  44. def CallbackArrayResultOneArrayByRef(self, arrayVal):
  45. ret = []
  46. for i in arrayVal:
  47. ret.append(i+1)
  48. # See above for list processing.
  49. return list(arrayVal), ret
  50. def CallbackResultOneByRefButReturnNone(self, intVal):
  51. return
  52. def CallbackVoidOneByRefButReturnNone(self, intVal):
  53. return
  54. def TestVB( vbtest, bUseGenerated ):
  55. vbtest.LongProperty = -1
  56. if vbtest.LongProperty != -1:
  57. raise error("Could not set the long property correctly.")
  58. vbtest.IntProperty = 10
  59. if vbtest.IntProperty != 10:
  60. raise error("Could not set the integer property correctly.")
  61. vbtest.VariantProperty = 10
  62. if vbtest.VariantProperty != 10:
  63. raise error("Could not set the variant integer property correctly.")
  64. vbtest.VariantProperty = str2memory('raw\0data')
  65. if vbtest.VariantProperty != str2memory('raw\0data'):
  66. raise error("Could not set the variant buffer property correctly.")
  67. vbtest.StringProperty = "Hello from Python"
  68. if vbtest.StringProperty != "Hello from Python":
  69. raise error("Could not set the string property correctly.")
  70. vbtest.VariantProperty = "Hello from Python"
  71. if vbtest.VariantProperty != "Hello from Python":
  72. raise error("Could not set the variant string property correctly.")
  73. vbtest.VariantProperty = (1.0, 2.0, 3.0)
  74. if vbtest.VariantProperty != (1.0, 2.0, 3.0):
  75. raise error("Could not set the variant property to an array of floats correctly - '%s'." % (vbtest.VariantProperty,))
  76. TestArrays(vbtest, bUseGenerated)
  77. TestStructs(vbtest)
  78. TestCollections(vbtest)
  79. assert vbtest.TakeByValObject(vbtest)==vbtest
  80. # Python doesnt support PUTREF properties without a typeref
  81. # (although we could)
  82. if bUseGenerated:
  83. ob = vbtest.TakeByRefObject(vbtest)
  84. assert ob[0]==vbtest and ob[1]==vbtest
  85. # A property that only has PUTREF defined.
  86. vbtest.VariantPutref = vbtest
  87. if vbtest.VariantPutref._oleobj_!= vbtest._oleobj_:
  88. raise error("Could not set the VariantPutref property correctly.")
  89. # Cant test further types for this VariantPutref, as only
  90. # COM objects can be stored ByRef.
  91. # A "set" type property - only works for generated.
  92. # VB recognizes a collection via a few "private" interfaces that we
  93. # could later build support in for.
  94. # vbtest.CollectionProperty = NewCollection((1,2,"3", "Four"))
  95. # if vbtest.CollectionProperty != (1,2,"3", "Four"):
  96. # raise error("Could not set the Collection property correctly - got back " + str(vbtest.CollectionProperty))
  97. # These are sub's that have a single byref param
  98. # Result should be just the byref.
  99. if vbtest.IncrementIntegerParam(1) != 2:
  100. raise error("Could not pass an integer byref")
  101. # Sigh - we cant have *both* "ommited byref" and optional args
  102. # We really have to opt that args nominated as optional work as optional
  103. # rather than simply all byrefs working as optional.
  104. # if vbtest.IncrementIntegerParam() != 1:
  105. # raise error("Could not pass an omitted integer byref")
  106. if vbtest.IncrementVariantParam(1) != 2:
  107. raise error("Could not pass an int VARIANT byref:"+str(vbtest.IncrementVariantParam(1)))
  108. if vbtest.IncrementVariantParam(1.5) != 2.5:
  109. raise error("Could not pass a float VARIANT byref")
  110. # Can't test IncrementVariantParam with the param omitted as it
  111. # it not declared in the VB code as "Optional"
  112. callback_ob = wrap(TestObject(), useDispatcher = useDispatcher)
  113. vbtest.DoSomeCallbacks(callback_ob)
  114. ret = vbtest.PassIntByVal(1)
  115. if ret != 2:
  116. raise error("Could not increment the integer - "+str(ret))
  117. TestVBInterface(vbtest)
  118. # Python doesnt support byrefs without some sort of generated support.
  119. if bUseGenerated:
  120. # This is a VB function that takes a single byref
  121. # Hence 2 return values - function and byref.
  122. ret = vbtest.PassIntByRef(1)
  123. if ret != (1,2):
  124. raise error("Could not increment the integer - "+str(ret))
  125. # Check you can leave a byref arg blank.
  126. # see above
  127. # ret = vbtest.PassIntByRef()
  128. # if ret != (0,1):
  129. # raise error("Could not increment the integer with default arg- "+str(ret))
  130. def _DoTestCollection(vbtest, col_name, expected):
  131. # It sucks that some objects allow "Count()", but others "Count"
  132. def _getcount(ob):
  133. r = getattr(ob, "Count")
  134. if type(r)!=type(0):
  135. return r()
  136. return r
  137. c = getattr(vbtest, col_name)
  138. check = []
  139. for item in c:
  140. check.append(item)
  141. if check != list(expected):
  142. raise error("Collection %s didn't have %r (had %r)" % (col_name, expected, check))
  143. # Just looping over the collection again works (ie, is restartable)
  144. check = []
  145. for item in c:
  146. check.append(item)
  147. if check != list(expected):
  148. raise error("Collection 2nd time around %s didn't have %r (had %r)" % (col_name, expected, check))
  149. # Check we can get it via iter()
  150. i = iter(getattr(vbtest, col_name))
  151. check = []
  152. for item in i:
  153. check.append(item)
  154. if check != list(expected):
  155. raise error("Collection iterator %s didn't have %r 2nd time around (had %r)" % (col_name, expected, check))
  156. # but an iterator is not restartable
  157. check = []
  158. for item in i:
  159. check.append(item)
  160. if check != []:
  161. raise error("2nd time around Collection iterator %s wasn't empty (had %r)" % (col_name, check))
  162. # Check len()==Count()
  163. c = getattr(vbtest, col_name)
  164. if len(c) != _getcount(c):
  165. raise error("Collection %s __len__(%r) wasn't==Count(%r)" % (col_name, len(c), _getcount(c)))
  166. # Check we can do it with zero based indexing.
  167. c = getattr(vbtest, col_name)
  168. check = []
  169. for i in range(_getcount(c)):
  170. check.append(c[i])
  171. if check != list(expected):
  172. raise error("Collection %s didn't have %r (had %r)" % (col_name, expected, check))
  173. # Check we can do it with our old "Skip/Next" methods.
  174. c = getattr(vbtest, col_name)._NewEnum()
  175. check = []
  176. while 1:
  177. n = c.Next()
  178. if not n:
  179. break
  180. check.append(n[0])
  181. if check != list(expected):
  182. raise error("Collection %s didn't have %r (had %r)" % (col_name, expected, check))
  183. def TestCollections(vbtest):
  184. _DoTestCollection(vbtest, "CollectionProperty", [1,"Two", "3"])
  185. # zero based indexing works for simple VB collections.
  186. if vbtest.CollectionProperty[0] != 1:
  187. raise error("The CollectionProperty[0] element was not the default value")
  188. _DoTestCollection(vbtest, "EnumerableCollectionProperty", [])
  189. vbtest.EnumerableCollectionProperty.Add(1)
  190. vbtest.EnumerableCollectionProperty.Add("Two")
  191. vbtest.EnumerableCollectionProperty.Add("3")
  192. _DoTestCollection(vbtest, "EnumerableCollectionProperty", [1,"Two", "3"])
  193. def _DoTestArray(vbtest, data, expected_exception = None):
  194. try:
  195. vbtest.ArrayProperty = data
  196. if expected_exception is not None:
  197. raise error("Expected '%s'" % expected_exception)
  198. except expected_exception:
  199. return
  200. got = vbtest.ArrayProperty
  201. if got != data:
  202. raise error(
  203. "Could not set the array data correctly - got %r, expected %r"
  204. % (got, data))
  205. def TestArrays(vbtest, bUseGenerated):
  206. # Try and use a safe array (note that the VB code has this declared as a VARIANT
  207. # and I cant work out how to force it to use native arrays!
  208. # (NOTE Python will convert incoming arrays to tuples, so we pass a tuple, even tho
  209. # a list works fine - just makes it easier for us to compare the result!
  210. # Empty array
  211. _DoTestArray(vbtest, ())
  212. # Empty child array
  213. _DoTestArray(vbtest, ((), ()))
  214. # ints
  215. _DoTestArray(vbtest, tuple(range(1,100)))
  216. # Floats
  217. _DoTestArray(vbtest, (1.0, 2.0, 3.0))
  218. # Strings.
  219. _DoTestArray(vbtest, tuple("Hello from Python".split()))
  220. # Date and Time?
  221. # COM objects.
  222. _DoTestArray(vbtest, (vbtest, vbtest))
  223. # Mixed
  224. _DoTestArray(vbtest, (1, 2.0, "3"))
  225. # Array alements containing other arrays
  226. _DoTestArray(vbtest, (1,(vbtest, vbtest),("3","4")))
  227. # Multi-dimensional
  228. _DoTestArray(vbtest, (( (1,2,3), (4,5,6) )))
  229. _DoTestArray(vbtest, (( (vbtest,vbtest,vbtest), (vbtest,vbtest,vbtest) )))
  230. # Another dimension!
  231. arrayData = ( ((1,2),(3,4),(5,6)), ((7,8),(9,10),(11,12)) )
  232. arrayData = ( ((vbtest,vbtest),(vbtest,vbtest),(vbtest,vbtest)),
  233. ((vbtest,vbtest),(vbtest,vbtest),(vbtest,vbtest)) )
  234. _DoTestArray(vbtest, arrayData)
  235. # Check that when a '__getitem__ that fails' object is the first item
  236. # in the structure, we don't mistake it for a sequence.
  237. _DoTestArray(vbtest, (vbtest, 2.0, "3"))
  238. _DoTestArray(vbtest, (1, 2.0, vbtest))
  239. # Pass arbitrarily sized arrays - these used to fail, but thanks to
  240. # Stefan Schukat, they now work!
  241. expected_exception = None
  242. arrayData = ( ((1,2,1),(3,4),(5,6)), ((7,8),(9,10),(11,12)) )
  243. _DoTestArray(vbtest, arrayData, expected_exception)
  244. arrayData = ( ((vbtest,vbtest),), ((vbtest,),))
  245. _DoTestArray(vbtest, arrayData, expected_exception)
  246. # Pass bad data - last item wrong size
  247. arrayData = ( ((1,2),(3,4),(5,6,8)), ((7,8),(9,10),(11,12)) )
  248. _DoTestArray(vbtest, arrayData, expected_exception)
  249. # byref safearray results with incorrect size.
  250. callback_ob = wrap(TestObject(), useDispatcher = useDispatcher)
  251. print("** Expecting a 'ValueError' exception to be printed next:")
  252. try:
  253. vbtest.DoCallbackSafeArraySizeFail(callback_ob)
  254. except pythoncom.com_error as exc:
  255. assert exc.excepinfo[1] == "Python COM Server Internal Error", "Didnt get the correct exception - '%s'" % (exc,)
  256. if bUseGenerated:
  257. # This one is a bit strange! The array param is "ByRef", as VB insists.
  258. # The function itself also _returns_ the arram param.
  259. # Therefore, Python sees _2_ result values - one for the result,
  260. # and one for the byref.
  261. testData = "Mark was here".split()
  262. resultData, byRefParam = vbtest.PassSAFEARRAY(testData)
  263. if testData != list(resultData):
  264. raise error("The safe array data was not what we expected - got " + str(resultData))
  265. if testData != list(byRefParam):
  266. raise error("The safe array data was not what we expected - got " + str(byRefParam))
  267. testData = [1.0, 2.0, 3.0]
  268. resultData, byRefParam = vbtest.PassSAFEARRAYVariant(testData)
  269. assert testData == list(byRefParam)
  270. assert testData == list(resultData)
  271. testData = ["hi", "from", "Python"]
  272. resultData, byRefParam = vbtest.PassSAFEARRAYVariant(testData)
  273. assert testData == list(byRefParam), "Expected '%s', got '%s'" % (testData, list(byRefParam))
  274. assert testData == list(resultData), "Expected '%s', got '%s'" % (testData, list(resultData))
  275. # This time, instead of an explicit str() for 1.5, we just
  276. # pass Unicode, so the result should compare equal
  277. testData = [1, 2.0, "3"]
  278. resultData, byRefParam = vbtest.PassSAFEARRAYVariant(testData)
  279. assert testData == list(byRefParam)
  280. assert testData == list(resultData)
  281. print("Array tests passed")
  282. def TestStructs(vbtest):
  283. try:
  284. vbtest.IntProperty = "One"
  285. raise error("Should have failed by now")
  286. except pythoncom.com_error as exc:
  287. if exc.hresult != winerror.DISP_E_TYPEMISMATCH:
  288. raise error("Expected DISP_E_TYPEMISMATCH")
  289. s = vbtest.StructProperty
  290. if s.int_val != 99 or str(s.str_val) != "hello":
  291. raise error("The struct value was not correct")
  292. s.str_val = "Hi from Python"
  293. s.int_val = 11
  294. if s.int_val != 11 or str(s.str_val) != "Hi from Python":
  295. raise error("The struct value didnt persist!")
  296. if s.sub_val.int_val != 66 or str(s.sub_val.str_val) != "sub hello":
  297. raise error("The sub-struct value was not correct")
  298. sub = s.sub_val
  299. sub.int_val = 22
  300. if sub.int_val != 22:
  301. print(sub.int_val)
  302. raise error("The sub-struct value didnt persist!")
  303. if s.sub_val.int_val != 22:
  304. print(s.sub_val.int_val)
  305. raise error("The sub-struct value (re-fetched) didnt persist!")
  306. if s.sub_val.array_val[0].int_val != 0 or str(s.sub_val.array_val[0].str_val) != "zero":
  307. print(s.sub_val.array_val[0].int_val)
  308. raise error("The array element wasnt correct")
  309. s.sub_val.array_val[0].int_val = 99
  310. s.sub_val.array_val[1].int_val = 66
  311. if s.sub_val.array_val[0].int_val != 99 or \
  312. s.sub_val.array_val[1].int_val != 66:
  313. print(s.sub_val.array_val[0].int_val)
  314. raise error("The array element didnt persist.")
  315. # Now pass the struct back to VB
  316. vbtest.StructProperty = s
  317. # And get it back again
  318. s = vbtest.StructProperty
  319. if s.int_val != 11 or str(s.str_val) != "Hi from Python":
  320. raise error("After sending to VB, the struct value didnt persist!")
  321. if s.sub_val.array_val[0].int_val != 99:
  322. raise error("After sending to VB, the struct array value didnt persist!")
  323. # Now do some object equality tests.
  324. assert s==s
  325. assert s!=None
  326. if sys.version_info > (3,0):
  327. try:
  328. s < None
  329. raise error("Expected type error")
  330. except TypeError:
  331. pass
  332. try:
  333. None < s
  334. raise error("Expected type error")
  335. except TypeError:
  336. pass
  337. assert s != s.sub_val
  338. import copy
  339. s2 = copy.copy(s)
  340. assert s is not s2
  341. assert s == s2
  342. s2.int_val = 123
  343. assert s != s2
  344. # Make sure everything works with functions
  345. s2 = vbtest.GetStructFunc()
  346. assert s==s2
  347. vbtest.SetStructSub(s2)
  348. # Create a new structure, and set its elements.
  349. s = win32com.client.Record("VBStruct", vbtest)
  350. assert s.int_val == 0, "new struct inst initialized correctly!"
  351. s.int_val = -1
  352. vbtest.SetStructSub(s)
  353. assert vbtest.GetStructFunc().int_val == -1, "new struct didnt make the round trip!"
  354. # Finally, test stand-alone structure arrays.
  355. s_array = vbtest.StructArrayProperty
  356. assert s_array is None, "Expected None from the uninitialized VB array"
  357. vbtest.MakeStructArrayProperty(3)
  358. s_array = vbtest.StructArrayProperty
  359. assert len(s_array)==3
  360. for i in range(len(s_array)):
  361. assert s_array[i].int_val == i
  362. assert s_array[i].sub_val.int_val == i
  363. assert s_array[i].sub_val.array_val[0].int_val == i
  364. assert s_array[i].sub_val.array_val[1].int_val == i+1
  365. assert s_array[i].sub_val.array_val[2].int_val == i+2
  366. # Some error type checks.
  367. try:
  368. s.bad_attribute
  369. raise RuntimeError("Could get a bad attribute")
  370. except AttributeError:
  371. pass
  372. m = s.__members__
  373. assert m[0]=="int_val" and m[1]=="str_val" and m[2]=="ob_val" and m[3]=="sub_val", m
  374. # Test attribute errors.
  375. try:
  376. s.foo
  377. raise RuntimeError("Expected attribute error")
  378. except AttributeError as exc:
  379. assert "foo" in str(exc), exc
  380. # test repr - it uses repr() of the sub-objects, so check it matches.
  381. 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)
  382. if repr(s) != expected:
  383. print("Expected repr:", expected)
  384. print("Actual repr :", repr(s))
  385. raise RuntimeError("repr() of record object failed")
  386. print("Struct/Record tests passed")
  387. def TestVBInterface(ob):
  388. t = ob.GetInterfaceTester(2)
  389. if t.getn() != 2:
  390. raise error("Initial value wrong")
  391. t.setn(3)
  392. if t.getn() != 3:
  393. raise error("New value wrong")
  394. def TestObjectSemantics(ob):
  395. # a convenient place to test some of our equality semantics
  396. assert ob==ob._oleobj_
  397. assert not ob!=ob._oleobj_
  398. # same test again, but lhs and rhs reversed.
  399. assert ob._oleobj_==ob
  400. assert not ob._oleobj_!=ob
  401. # same tests but against different pointers. COM identity rules should
  402. # still ensure all works
  403. assert ob._oleobj_==ob._oleobj_.QueryInterface(pythoncom.IID_IUnknown)
  404. assert not ob._oleobj_!=ob._oleobj_.QueryInterface(pythoncom.IID_IUnknown)
  405. assert ob._oleobj_!=None
  406. assert None!=ob._oleobj_
  407. assert ob!=None
  408. assert None!=ob
  409. if sys.version_info > (3,0):
  410. try:
  411. ob < None
  412. raise error("Expected type error")
  413. except TypeError:
  414. pass
  415. try:
  416. None < ob
  417. raise error("Expected type error")
  418. except TypeError:
  419. pass
  420. assert ob._oleobj_.QueryInterface(pythoncom.IID_IUnknown)==ob._oleobj_
  421. assert not ob._oleobj_.QueryInterface(pythoncom.IID_IUnknown)!=ob._oleobj_
  422. assert ob._oleobj_==ob._oleobj_.QueryInterface(pythoncom.IID_IDispatch)
  423. assert not ob._oleobj_!=ob._oleobj_.QueryInterface(pythoncom.IID_IDispatch)
  424. assert ob._oleobj_.QueryInterface(pythoncom.IID_IDispatch)==ob._oleobj_
  425. assert not ob._oleobj_.QueryInterface(pythoncom.IID_IDispatch)!=ob._oleobj_
  426. print("Object semantic tests passed")
  427. def DoTestAll():
  428. o = win32com.client.Dispatch("PyCOMVBTest.Tester")
  429. TestObjectSemantics(o)
  430. TestVB(o,1)
  431. o = win32com.client.dynamic.DumbDispatch("PyCOMVBTest.Tester")
  432. TestObjectSemantics(o)
  433. TestVB(o,0)
  434. def TestAll():
  435. # Import the type library for the test module. Let the 'invalid clsid'
  436. # exception filter up, where the test runner will treat it as 'skipped'
  437. win32com.client.gencache.EnsureDispatch("PyCOMVBTest.Tester")
  438. if not __debug__:
  439. raise RuntimeError("This must be run in debug mode - we use assert!")
  440. try:
  441. DoTestAll()
  442. print("All tests appear to have worked!")
  443. except:
  444. # ?????
  445. print("TestAll() failed!!")
  446. traceback.print_exc()
  447. raise
  448. # Make this test run under our test suite to leak tests etc work
  449. def suite():
  450. import unittest
  451. test = util.CapturingFunctionTestCase(TestAll, description="VB tests")
  452. suite = unittest.TestSuite()
  453. suite.addTest(test)
  454. return suite
  455. if __name__=='__main__':
  456. util.testmain()