Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

testvb.py 20KB

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