mparticle-roku-sdk 2.1.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. package/.github/workflows/build.yml +26 -0
  2. package/.gitmodules +3 -0
  3. package/LICENSE +191 -0
  4. package/README.md +24 -0
  5. package/example-legacy-sdk/images/MainMenu_Icon_Center_HD.png +0 -0
  6. package/example-legacy-sdk/images/MainMenu_Icon_Center_SD43.png +0 -0
  7. package/example-legacy-sdk/images/MainMenu_Icon_Side_HD.png +0 -0
  8. package/example-legacy-sdk/images/MainMenu_Icon_Side_SD43.png +0 -0
  9. package/example-legacy-sdk/images/splash_fhd.jpg +0 -0
  10. package/example-legacy-sdk/images/splash_hd.jpg +0 -0
  11. package/example-legacy-sdk/images/splash_sd.jpg +0 -0
  12. package/example-legacy-sdk/manifest +26 -0
  13. package/example-legacy-sdk/source/main.brs +45 -0
  14. package/example-scenegraph-sdk/LICENSE +1 -0
  15. package/example-scenegraph-sdk/README.md +2 -0
  16. package/example-scenegraph-sdk/source/Makefile +6 -0
  17. package/example-scenegraph-sdk/source/app.mk +675 -0
  18. package/example-scenegraph-sdk/source/components/helloworld.brs +300 -0
  19. package/example-scenegraph-sdk/source/components/helloworld.xml +66 -0
  20. package/example-scenegraph-sdk/source/images/channel-poster_fhd.png +0 -0
  21. package/example-scenegraph-sdk/source/images/channel-poster_hd.png +0 -0
  22. package/example-scenegraph-sdk/source/images/channel-poster_sd.png +0 -0
  23. package/example-scenegraph-sdk/source/images/splash-screen_fhd.jpg +0 -0
  24. package/example-scenegraph-sdk/source/images/splash-screen_hd.jpg +0 -0
  25. package/example-scenegraph-sdk/source/images/splash-screen_sd.jpg +0 -0
  26. package/example-scenegraph-sdk/source/manifest +12 -0
  27. package/example-scenegraph-sdk/source/source/Main.brs +46 -0
  28. package/example-scenegraph-sdk/source/testFramework/UnitTestFramework.brs +2867 -0
  29. package/example-scenegraph-sdk/source/tests/TestBasics.brs +266 -0
  30. package/mParticleBundle.crt +68 -0
  31. package/mParticleCore.brs +2301 -0
  32. package/mParticleTask.brs +78 -0
  33. package/mParticleTask.xml +12 -0
  34. package/package.json +5 -0
  35. package/testing/tests/Test__mParticle.brs +88 -0
@@ -0,0 +1,2867 @@
1
+ '*****************************************************************
2
+ '* Roku Unit Testing Framework
3
+ '* Automating test suites for Roku channels.
4
+ '*
5
+ '* Build Version: 2.1.1
6
+ '* Build Date: 05/06/2019
7
+ '*
8
+ '* Public Documentation is avaliable on GitHub:
9
+ '* https://github.com/rokudev/unit-testing-framework
10
+ '*
11
+ '*****************************************************************
12
+ '*****************************************************************
13
+ '* Copyright Roku 2011-2019
14
+ '* All Rights Reserved
15
+ '*****************************************************************
16
+
17
+ ' Functions in this file:
18
+
19
+ ' BaseTestSuite
20
+ ' BTS__AddTest
21
+ ' BTS__CreateTest
22
+ ' BTS__Fail
23
+ ' BTS__AssertFalse
24
+ ' BTS__AssertTrue
25
+ ' BTS__AssertEqual
26
+ ' BTS__AssertNotEqual
27
+ ' BTS__AssertInvalid
28
+ ' BTS__AssertNotInvalid
29
+ ' BTS__AssertAAHasKey
30
+ ' BTS__AssertAANotHasKey
31
+ ' BTS__AssertAAHasKeys
32
+ ' BTS__AssertAANotHasKeys
33
+ ' BTS__AssertArrayContains
34
+ ' BTS__AssertArrayNotContains
35
+ ' BTS__AssertArrayContainsSubset
36
+ ' BTS__AssertArrayNotContainsSubset
37
+ ' BTS__AssertArrayCount
38
+ ' BTS__AssertArrayNotCount
39
+ ' BTS__AssertEmpty
40
+ ' BTS__AssertNotEmpty
41
+
42
+ ' ----------------------------------------------------------------
43
+ ' Main function. Create BaseTestSuite object.
44
+
45
+ ' @return A BaseTestSuite object.
46
+ ' ----------------------------------------------------------------
47
+ function BaseTestSuite()
48
+ this = {}
49
+ this.Name = "BaseTestSuite"
50
+ this.SKIP_TEST_MESSAGE_PREFIX = "SKIP_TEST_MESSAGE_PREFIX__"
51
+ ' Test Cases methods
52
+ this.testCases = []
53
+ this.IS_NEW_APPROACH = false
54
+ this.addTest = BTS__AddTest
55
+ this.createTest = BTS__CreateTest
56
+ this.StorePerformanceData = BTS__StorePerformanceData
57
+
58
+ ' Assertion methods which determine test failure or skipping
59
+ this.skip = BTS__Skip
60
+ this.fail = BTS__Fail
61
+ this.assertFalse = BTS__AssertFalse
62
+ this.assertTrue = BTS__AssertTrue
63
+ this.assertEqual = BTS__AssertEqual
64
+ this.assertNotEqual = BTS__AssertNotEqual
65
+ this.assertInvalid = BTS__AssertInvalid
66
+ this.assertNotInvalid = BTS__AssertNotInvalid
67
+ this.assertAAHasKey = BTS__AssertAAHasKey
68
+ this.assertAANotHasKey = BTS__AssertAANotHasKey
69
+ this.assertAAHasKeys = BTS__AssertAAHasKeys
70
+ this.assertAANotHasKeys = BTS__AssertAANotHasKeys
71
+ this.assertArrayContains = BTS__AssertArrayContains
72
+ this.assertArrayNotContains = BTS__AssertArrayNotContains
73
+ this.assertArrayContainsSubset = BTS__AssertArrayContainsSubset
74
+ this.assertArrayNotContainsSubset = BTS__AssertArrayNotContainsSubset
75
+ this.assertArrayCount = BTS__AssertArrayCount
76
+ this.assertArrayNotCount = BTS__AssertArrayNotCount
77
+ this.assertEmpty = BTS__AssertEmpty
78
+ this.assertNotEmpty = BTS__AssertNotEmpty
79
+
80
+ ' Type Comparison Functionality
81
+ this.eqValues = TF_Utils__EqValues
82
+ this.eqAssocArrays = TF_Utils__EqAssocArray
83
+ this.eqArrays = TF_Utils__EqArray
84
+ this.baseComparator = TF_Utils__BaseComparator
85
+
86
+ return this
87
+ end function
88
+
89
+ ' ----------------------------------------------------------------
90
+ ' Add a test to a suite's test cases array.
91
+
92
+ ' @param name (string) A test name.
93
+ ' @param func (object) A pointer to test function.
94
+ ' @param setup (object) A pointer to setup function.
95
+ ' @param teardown (object) A pointer to teardown function.
96
+ ' @param arg (dynamic) A test function arguments.
97
+ ' @param hasArgs (boolean) True if test function has parameters.
98
+ ' @param skip (boolean) Skip test run.
99
+ ' ----------------------------------------------------------------
100
+ sub BTS__AddTest(name as string, func as object, setup = invalid as object, teardown = invalid as object, arg = invalid as dynamic, hasArgs = false as boolean, skip = false as boolean)
101
+ m.testCases.Push(m.createTest(name, func, setup, teardown, arg, hasArgs, skip))
102
+ end sub
103
+
104
+ ' ----------------------------------------------------------------
105
+ ' Create a test object.
106
+
107
+ ' @param name (string) A test name.
108
+ ' @param func (object) A pointer to test function.
109
+ ' @param setup (object) A pointer to setup function.
110
+ ' @param teardown (object) A pointer to teardown function.
111
+ ' @param arg (dynamic) A test function arguments.
112
+ ' @param hasArgs (boolean) True if test function has parameters.
113
+ ' @param skip (boolean) Skip test run.
114
+ '
115
+ ' @return TestCase object.
116
+ ' ----------------------------------------------------------------
117
+ function BTS__CreateTest(name as string, func as object, setup = invalid as object, teardown = invalid as object, arg = invalid as dynamic, hasArgs = false as boolean, skip = false as boolean) as object
118
+ return {
119
+ Name: name
120
+ Func: func
121
+ SetUp: setup
122
+ TearDown: teardown
123
+
124
+ perfData: {}
125
+
126
+ hasArguments: hasArgs
127
+ arg: arg
128
+
129
+ skip: skip
130
+ }
131
+ end function
132
+
133
+ '----------------------------------------------------------------
134
+ ' Store performance data to current test instance.
135
+ '
136
+ ' @param name (string) A property name.
137
+ ' @param value (Object) A value of data.
138
+ '----------------------------------------------------------------
139
+ sub BTS__StorePerformanceData(name as string, value as object)
140
+ timestamp = StrI(CreateObject("roDateTime").AsSeconds())
141
+ m.testInstance.perfData.Append({
142
+ name: {
143
+ "value": value
144
+ "timestamp": timestamp
145
+ }
146
+ })
147
+ ' print performance data to console
148
+ ? "PERF_DATA: " + m.testInstance.Name + ": " + timestamp + ": " + name + "|" + TF_Utils__AsString(value)
149
+ end sub
150
+
151
+ ' ----------------------------------------------------------------
152
+ ' Assertion methods which determine test failure or skipping
153
+ ' ----------------------------------------------------------------
154
+
155
+ ' ----------------------------------------------------------------
156
+ ' Should be used to skip test cases. To skip test you must return the result of this method invocation.
157
+
158
+ ' @param message (string) Optional skip message.
159
+ ' Default value: "".
160
+
161
+ ' @return A skip message, with a specific prefix added, in order to runner know that this test should be skipped.
162
+ ' ----------------------------------------------------------------
163
+ function BTS__Skip(message = "" as string) as string
164
+ ' add prefix so we know that this test is skipped, but not failed
165
+ return m.SKIP_TEST_MESSAGE_PREFIX + message
166
+ end function
167
+
168
+ ' ----------------------------------------------------------------
169
+ ' Fail immediately, with the given message
170
+
171
+ ' @param msg (string) An error message.
172
+ ' Default value: "Error".
173
+
174
+ ' @return An error message.
175
+ ' ----------------------------------------------------------------
176
+ function BTS__Fail(msg = "Error" as string) as string
177
+ return msg
178
+ end function
179
+
180
+ ' ----------------------------------------------------------------
181
+ ' Fail the test if the expression is true.
182
+
183
+ ' @param expr (dynamic) An expression to evaluate.
184
+ ' @param msg (string) An error message.
185
+ ' Default value: "Expression evaluates to true"
186
+
187
+ ' @return An error message.
188
+ ' ----------------------------------------------------------------
189
+ function BTS__AssertFalse(expr as dynamic, msg = "Expression evaluates to true" as string) as string
190
+ if not TF_Utils__IsBoolean(expr) or expr
191
+ return BTS__Fail(msg)
192
+ end if
193
+ return ""
194
+ end function
195
+
196
+ ' ----------------------------------------------------------------
197
+ ' Fail the test unless the expression is true.
198
+
199
+ ' @param expr (dynamic) An expression to evaluate.
200
+ ' @param msg (string) An error message.
201
+ ' Default value: "Expression evaluates to false"
202
+
203
+ ' @return An error message.
204
+ ' ----------------------------------------------------------------
205
+ function BTS__AssertTrue(expr as dynamic, msg = "Expression evaluates to false" as string) as string
206
+ if not TF_Utils__IsBoolean(expr) or not expr then
207
+ return msg
208
+ end if
209
+ return ""
210
+ end function
211
+
212
+ ' ----------------------------------------------------------------
213
+ ' Fail if the two objects are unequal as determined by the '<>' operator.
214
+
215
+ ' @param first (dynamic) A first object to compare.
216
+ ' @param second (dynamic) A second object to compare.
217
+ ' @param msg (string) An error message.
218
+ ' Default value: ""
219
+
220
+ ' @return An error message.
221
+ ' ----------------------------------------------------------------
222
+ function BTS__AssertEqual(first as dynamic, second as dynamic, msg = "" as string) as string
223
+ if not TF_Utils__EqValues(first, second)
224
+ if msg = ""
225
+ first_as_string = TF_Utils__AsString(first)
226
+ second_as_string = TF_Utils__AsString(second)
227
+ msg = first_as_string + " != " + second_as_string
228
+ end if
229
+ return msg
230
+ end if
231
+ return ""
232
+ end function
233
+
234
+ ' ----------------------------------------------------------------
235
+ ' Fail if the two objects are equal as determined by the '=' operator.
236
+
237
+ ' @param first (dynamic) A first object to compare.
238
+ ' @param second (dynamic) A second object to compare.
239
+ ' @param msg (string) An error message.
240
+ ' Default value: ""
241
+
242
+ ' @return An error message.
243
+ ' ----------------------------------------------------------------
244
+ function BTS__AssertNotEqual(first as dynamic, second as dynamic, msg = "" as string) as string
245
+ if TF_Utils__EqValues(first, second)
246
+ if msg = ""
247
+ first_as_string = TF_Utils__AsString(first)
248
+ second_as_string = TF_Utils__AsString(second)
249
+ msg = first_as_string + " == " + second_as_string
250
+ end if
251
+ return msg
252
+ end if
253
+ return ""
254
+ end function
255
+
256
+ ' ----------------------------------------------------------------
257
+ ' Fail if the value is not invalid.
258
+
259
+ ' @param value (dynamic) A value to check.
260
+ ' @param msg (string) An error message.
261
+ ' Default value: ""
262
+
263
+ ' @return An error message.
264
+ ' ----------------------------------------------------------------
265
+ function BTS__AssertInvalid(value as dynamic, msg = "" as string) as string
266
+ if TF_Utils__IsValid(value)
267
+ if msg = ""
268
+ expr_as_string = TF_Utils__AsString(value)
269
+ msg = expr_as_string + " <> Invalid"
270
+ end if
271
+ return msg
272
+ end if
273
+ return ""
274
+ end function
275
+
276
+ ' ----------------------------------------------------------------
277
+ ' Fail if the value is invalid.
278
+
279
+ ' @param value (dynamic) A value to check.
280
+ ' @param msg (string) An error message.
281
+ ' Default value: ""
282
+
283
+ ' @return An error message.
284
+ ' ----------------------------------------------------------------
285
+ function BTS__AssertNotInvalid(value as dynamic, msg = "" as string) as string
286
+ if not TF_Utils__IsValid(value)
287
+ if msg = ""
288
+ if LCase(Type(value)) = "<uninitialized>" then value = invalid
289
+ expr_as_string = TF_Utils__AsString(value)
290
+ msg = expr_as_string + " = Invalid"
291
+ end if
292
+ return msg
293
+ end if
294
+ return ""
295
+ end function
296
+
297
+ ' ----------------------------------------------------------------
298
+ ' Fail if the array doesn't have the key.
299
+
300
+ ' @param array (dynamic) A target array.
301
+ ' @param key (string) A key name.
302
+ ' @param msg (string) An error message.
303
+ ' Default value: ""
304
+
305
+ ' @return An error message.
306
+ ' ----------------------------------------------------------------
307
+ function BTS__AssertAAHasKey(array as dynamic, key as dynamic, msg = "" as string) as string
308
+ if not TF_Utils__IsString(key)
309
+ return "Key value has invalid type."
310
+ end if
311
+
312
+ if TF_Utils__IsAssociativeArray(array)
313
+ if not array.DoesExist(key)
314
+ if msg = ""
315
+ msg = "Array doesn't have the '" + key + "' key."
316
+ end if
317
+ return msg
318
+ end if
319
+ else
320
+ msg = "Input value is not an Associative Array."
321
+ return msg
322
+ end if
323
+
324
+ return ""
325
+ end function
326
+
327
+ ' ----------------------------------------------------------------
328
+ ' Fail if the array has the key.
329
+
330
+ ' @param array (dynamic) A target array.
331
+ ' @param key (string) A key name.
332
+ ' @param msg (string) An error message.
333
+ ' Default value: ""
334
+
335
+ ' @return An error message.
336
+ ' ----------------------------------------------------------------
337
+ function BTS__AssertAANotHasKey(array as dynamic, key as dynamic, msg = "" as string) as string
338
+ if not TF_Utils__IsString(key)
339
+ return "Key value has invalid type."
340
+ end if
341
+
342
+ if TF_Utils__IsAssociativeArray(array)
343
+ if array.DoesExist(key)
344
+ if msg = ""
345
+ msg = "Array has the '" + key + "' key."
346
+ end if
347
+ return msg
348
+ end if
349
+ else
350
+ msg = "Input value is not an Associative Array."
351
+ return msg
352
+ end if
353
+
354
+ return ""
355
+ end function
356
+
357
+ ' ----------------------------------------------------------------
358
+ ' Fail if the array doesn't have the keys list.
359
+
360
+ ' @param array (dynamic) A target associative array.
361
+ ' @param keys (object) A key names array.
362
+ ' @param msg (string) An error message.
363
+ ' Default value: ""
364
+
365
+ ' @return An error message.
366
+ ' ----------------------------------------------------------------
367
+ function BTS__AssertAAHasKeys(array as dynamic, keys as object, msg = "" as string) as string
368
+ if not TF_Utils__IsAssociativeArray(array)
369
+ return "Input value is not an Associative Array."
370
+ end if
371
+
372
+ if not TF_Utils__IsArray(keys) or keys.Count() = 0
373
+ return "Keys value is not an Array or is empty."
374
+ end if
375
+
376
+ if TF_Utils__IsAssociativeArray(array) and TF_Utils__IsArray(keys)
377
+ for each key in keys
378
+ if not TF_Utils__IsString(key)
379
+ return "Key value has invalid type."
380
+ end if
381
+
382
+ if not array.DoesExist(key)
383
+ if msg = ""
384
+ msg = "Array doesn't have the '" + key + "' key."
385
+ end if
386
+
387
+ return msg
388
+ end if
389
+ end for
390
+ else
391
+ msg = "Input value is not an Associative Array."
392
+ return msg
393
+ end if
394
+
395
+ return ""
396
+ end function
397
+
398
+ ' ----------------------------------------------------------------
399
+ ' Fail if the array has the keys list.
400
+
401
+ ' @param array (dynamic) A target associative array.
402
+ ' @param keys (object) A key names array.
403
+ ' @param msg (string) An error message.
404
+ ' Default value: ""
405
+
406
+ ' @return An error message.
407
+ ' ----------------------------------------------------------------
408
+ function BTS__AssertAANotHasKeys(array as dynamic, keys as object, msg = "" as string) as string
409
+ if not TF_Utils__IsAssociativeArray(array)
410
+ return "Input value is not an Associative Array."
411
+ end if
412
+
413
+ if not TF_Utils__IsArray(keys) or keys.Count() = 0
414
+ return "Keys value is not an Array or is empty."
415
+ end if
416
+
417
+ if TF_Utils__IsAssociativeArray(array) and TF_Utils__IsArray(keys)
418
+ for each key in keys
419
+ if not TF_Utils__IsString(key)
420
+ return "Key value has invalid type."
421
+ end if
422
+
423
+ if array.DoesExist(key)
424
+ if msg = ""
425
+ msg = "Array has the '" + key + "' key."
426
+ end if
427
+ return msg
428
+ end if
429
+ end for
430
+ else
431
+ msg = "Input value is not an Associative Array."
432
+ return msg
433
+ end if
434
+ return ""
435
+ end function
436
+
437
+ ' ----------------------------------------------------------------
438
+ ' Fail if the array doesn't have the item.
439
+
440
+ ' @param array (dynamic) A target array.
441
+ ' @param value (dynamic) A value to check.
442
+ ' @param key (object) A key name for associative array.
443
+ ' @param msg (string) An error message.
444
+ ' Default value: ""
445
+
446
+ ' @return An error message.
447
+ ' ----------------------------------------------------------------
448
+ function BTS__AssertArrayContains(array as dynamic, value as dynamic, key = invalid as dynamic, msg = "" as string) as string
449
+ if key <> invalid and not TF_Utils__IsString(key)
450
+ return "Key value has invalid type."
451
+ end if
452
+
453
+ if TF_Utils__IsAssociativeArray(array) or TF_Utils__IsArray(array)
454
+ if not TF_Utils__ArrayContains(array, value, key)
455
+ msg = "Array doesn't have the '" + TF_Utils__AsString(value) + "' value."
456
+
457
+ return msg
458
+ end if
459
+ else
460
+ msg = "Input value is not an Array."
461
+
462
+ return msg
463
+ end if
464
+
465
+ return ""
466
+ end function
467
+
468
+ ' ----------------------------------------------------------------
469
+ ' Fail if the array has the item.
470
+
471
+ ' @param array (dynamic) A target array.
472
+ ' @param value (dynamic) A value to check.
473
+ ' @param key (object) A key name for associative array.
474
+ ' @param msg (string) An error message.
475
+ ' Default value: ""
476
+
477
+ ' @return An error message.
478
+ ' ----------------------------------------------------------------
479
+ function BTS__AssertArrayNotContains(array as dynamic, value as dynamic, key = invalid as dynamic, msg = "" as string) as string
480
+ if key <> invalid and not TF_Utils__IsString(key)
481
+ return "Key value has invalid type."
482
+ end if
483
+
484
+ if TF_Utils__IsAssociativeArray(array) or TF_Utils__IsArray(array)
485
+ if TF_Utils__ArrayContains(array, value, key)
486
+ msg = "Array has the '" + TF_Utils__AsString(value) + "' value."
487
+
488
+ return msg
489
+ end if
490
+ else
491
+ msg = "Input value is not an Array."
492
+
493
+ return msg
494
+ end if
495
+
496
+ return ""
497
+ end function
498
+
499
+ ' ----------------------------------------------------------------
500
+ ' Fail if the array doesn't have the item subset.
501
+
502
+ ' @param array (dynamic) A target array.
503
+ ' @param subset (dynamic) An items array to check.
504
+ ' @param msg (string) An error message.
505
+ ' Default value: ""
506
+
507
+ ' @return An error message.
508
+ ' ----------------------------------------------------------------
509
+ function BTS__AssertArrayContainsSubset(array as dynamic, subset as dynamic, msg = "" as string) as string
510
+ if (TF_Utils__IsAssociativeArray(array) and TF_Utils__IsAssociativeArray(subset)) or (TF_Utils__IsArray(array) and TF_Utils__IsArray(subset))
511
+ isAA = TF_Utils__IsAssociativeArray(subset)
512
+ for each item in subset
513
+ key = invalid
514
+ value = item
515
+ if isAA
516
+ key = item
517
+ value = subset[key]
518
+ end if
519
+
520
+ if not TF_Utils__ArrayContains(array, value, key)
521
+ msg = "Array doesn't have the '" + TF_Utils__AsString(value) + "' value."
522
+
523
+ return msg
524
+ end if
525
+ end for
526
+ else
527
+ msg = "Input value is not an Array."
528
+
529
+ return msg
530
+ end if
531
+
532
+ return ""
533
+ end function
534
+
535
+ ' ----------------------------------------------------------------
536
+ ' Fail if the array have the item from subset.
537
+
538
+ ' @param array (dynamic) A target array.
539
+ ' @param subset (dynamic) A items array to check.
540
+ ' @param msg (string) An error message.
541
+ ' Default value: ""
542
+
543
+ ' @return An error message.
544
+ ' ----------------------------------------------------------------
545
+ function BTS__AssertArrayNotContainsSubset(array as dynamic, subset as dynamic, msg = "" as string) as string
546
+ if (TF_Utils__IsAssociativeArray(array) and TF_Utils__IsAssociativeArray(subset)) or (TF_Utils__IsArray(array) and TF_Utils__IsArray(subset))
547
+ isAA = TF_Utils__IsAssociativeArray(subset)
548
+ for each item in subset
549
+ key = invalid
550
+ value = item
551
+ if isAA
552
+ key = item
553
+ value = subset[key]
554
+ end if
555
+
556
+ if TF_Utils__ArrayContains(array, value, key)
557
+ msg = "Array has the '" + TF_Utils__AsString(value) + "' value."
558
+
559
+ return msg
560
+ end if
561
+ end for
562
+ else
563
+ msg = "Input value is not an Array."
564
+
565
+ return msg
566
+ end if
567
+
568
+ return ""
569
+ end function
570
+
571
+ ' ----------------------------------------------------------------
572
+ ' Fail if the array items count <> expected count
573
+
574
+ ' @param array (dynamic) A target array.
575
+ ' @param count (integer) An expected array items count.
576
+ ' @param msg (string) An error message.
577
+ ' Default value: ""
578
+
579
+ ' @return An error message.
580
+ ' ----------------------------------------------------------------
581
+ function BTS__AssertArrayCount(array as dynamic, count as dynamic, msg = "" as string) as string
582
+ if not TF_Utils__IsInteger(count)
583
+ return "Count value should be an integer."
584
+ end if
585
+
586
+ if TF_Utils__IsAssociativeArray(array) or TF_Utils__IsArray(array)
587
+ if array.Count() <> count
588
+ msg = "Array items count <> " + TF_Utils__AsString(count) + "."
589
+
590
+ return msg
591
+ end if
592
+ else
593
+ msg = "Input value is not an Array."
594
+
595
+ return msg
596
+ end if
597
+
598
+ return ""
599
+ end function
600
+
601
+ ' ----------------------------------------------------------------
602
+ ' Fail if the array items count = expected count.
603
+
604
+ ' @param array (dynamic) A target array.
605
+ ' @param count (integer) An expected array items count.
606
+ ' @param msg (string) An error message.
607
+ ' Default value: ""
608
+
609
+ ' @return An error message.
610
+ ' ----------------------------------------------------------------
611
+ function BTS__AssertArrayNotCount(array as dynamic, count as dynamic, msg = "" as string) as string
612
+ if not TF_Utils__IsInteger(count)
613
+ return "Count value should be an integer."
614
+ end if
615
+
616
+ if TF_Utils__IsAssociativeArray(array) or TF_Utils__IsArray(array)
617
+ if array.Count() = count
618
+ msg = "Array items count = " + TF_Utils__AsString(count) + "."
619
+
620
+ return msg
621
+ end if
622
+ else
623
+ msg = "Input value is not an Array."
624
+
625
+ return msg
626
+ end if
627
+
628
+ return ""
629
+ end function
630
+
631
+ ' ----------------------------------------------------------------
632
+ ' Fail if the item is not empty array or string.
633
+
634
+ ' @param item (dynamic) An array or string to check.
635
+ ' @param msg (string) An error message.
636
+ ' Default value: ""
637
+
638
+ ' @return An error message.
639
+ ' ----------------------------------------------------------------
640
+ function BTS__AssertEmpty(item as dynamic, msg = "" as string) as string
641
+ if TF_Utils__IsAssociativeArray(item) or TF_Utils__IsArray(item)
642
+ if item.Count() > 0
643
+ msg = "Array is not empty."
644
+
645
+ return msg
646
+ end if
647
+ else if TF_Utils__IsString(item)
648
+ if Len(item) <> 0
649
+ msg = "Input value is not empty."
650
+
651
+ return msg
652
+ end if
653
+ else
654
+ msg = "Input value is not an Array, AssociativeArray or String."
655
+
656
+ return msg
657
+ end if
658
+
659
+ return ""
660
+ end function
661
+
662
+ ' ----------------------------------------------------------------
663
+ ' Fail if the item is empty array or string.
664
+
665
+ ' @param item (dynamic) An array or string to check.
666
+ ' @param msg (string) An error message.
667
+ ' Default value: ""
668
+
669
+ ' @return An error message.
670
+ ' ----------------------------------------------------------------
671
+ function BTS__AssertNotEmpty(item as dynamic, msg = "" as string) as string
672
+ if TF_Utils__IsAssociativeArray(item) or TF_Utils__IsArray(item)
673
+ if item.Count() = 0
674
+ msg = "Array is empty."
675
+
676
+ return msg
677
+ end if
678
+ else if TF_Utils__IsString(item)
679
+ if Len(item) = 0
680
+ msg = "Input value is empty."
681
+
682
+ return msg
683
+ end if
684
+ else
685
+ msg = "Input value is not an Array, AssociativeArray or String."
686
+
687
+ return msg
688
+ end if
689
+
690
+ return ""
691
+ end function
692
+
693
+ '*****************************************************************
694
+ '* Copyright Roku 2011-2019
695
+ '* All Rights Reserved
696
+ '*****************************************************************
697
+
698
+ ' Functions in this file:
699
+ ' ItemGenerator
700
+ ' IG_GetItem
701
+ ' IG_GetAssocArray
702
+ ' IG_GetArray
703
+ ' IG_GetSimpleType
704
+ ' IG_GetBoolean
705
+ ' IG_GetInteger
706
+ ' IG_GetFloat
707
+ ' IG_GetString
708
+
709
+ ' ----------------------------------------------------------------
710
+ ' Main function to generate object according to specified scheme.
711
+
712
+ ' @param scheme (object) A scheme with desired object structure. Can be
713
+ ' any simple type, array of types or associative array in form
714
+ ' { propertyName1 : "propertyType1"
715
+ ' propertyName2 : "propertyType2"
716
+ ' ...
717
+ ' propertyNameN : "propertyTypeN" }
718
+
719
+ ' @return An object according to specified scheme or invalid,
720
+ ' if scheme is not valid.
721
+ ' ----------------------------------------------------------------
722
+ function ItemGenerator(scheme as object) as object
723
+ this = {}
724
+
725
+ this.getItem = IG_GetItem
726
+ this.getAssocArray = IG_GetAssocArray
727
+ this.getArray = IG_GetArray
728
+ this.getSimpleType = IG_GetSimpleType
729
+ this.getInteger = IG_GetInteger
730
+ this.getFloat = IG_GetFloat
731
+ this.getString = IG_GetString
732
+ this.getBoolean = IG_GetBoolean
733
+
734
+ if not TF_Utils__IsValid(scheme)
735
+ return invalid
736
+ end if
737
+
738
+ return this.getItem(scheme)
739
+ end function
740
+
741
+ ' TODO: Create IG_GetInvalidItem function with random type fields
742
+
743
+ ' ----------------------------------------------------------------
744
+ ' Generate object according to specified scheme.
745
+
746
+ ' @param scheme (object) A scheme with desired object structure.
747
+ ' Can be any simple type, array of types or associative array.
748
+
749
+ ' @return An object according to specified scheme or invalid,
750
+ ' if scheme is not one of simple type, array or
751
+ ' associative array.
752
+ ' ----------------------------------------------------------------
753
+ function IG_GetItem(scheme as object) as object
754
+ item = invalid
755
+
756
+ if TF_Utils__IsAssociativeArray(scheme)
757
+ item = IG_GetAssocArray(scheme)
758
+ else if TF_Utils__IsArray(scheme)
759
+ item = IG_GetArray(scheme)
760
+ else if TF_Utils__IsString(scheme)
761
+ item = IG_GetSimpleType(LCase(scheme))
762
+ end if
763
+
764
+ return item
765
+ end function
766
+
767
+ ' ----------------------------------------------------------------
768
+ ' Generates associative array according to specified scheme.
769
+
770
+ ' @param scheme (object) An associative array with desired
771
+ ' object structure in form
772
+ ' { propertyName1 : "propertyType1"
773
+ ' propertyName2 : "propertyType2"
774
+ ' ...
775
+ ' propertyNameN : "propertyTypeN" }
776
+
777
+ ' @return An associative array according to specified scheme.
778
+ ' ----------------------------------------------------------------
779
+ function IG_GetAssocArray(scheme as object) as object
780
+ item = {}
781
+
782
+ for each key in scheme
783
+ if not item.DoesExist(key)
784
+ item[key] = IG_GetItem(scheme[key])
785
+ end if
786
+ end for
787
+
788
+ return item
789
+ end function
790
+
791
+ ' ----------------------------------------------------------------
792
+ ' Generates array according to specified scheme.
793
+
794
+ ' @param scheme (object) An array with desired object types.
795
+
796
+ ' @return An array according to specified scheme.
797
+ ' ----------------------------------------------------------------
798
+ function IG_GetArray(scheme as object) as object
799
+ item = []
800
+
801
+ for each key in scheme
802
+ item.Push(IG_GetItem(key))
803
+ end for
804
+
805
+ return item
806
+ end function
807
+
808
+ ' ----------------------------------------------------------------
809
+ ' Generates random value of specified type.
810
+
811
+ ' @param typeStr (string) A name of desired object type.
812
+
813
+ ' @return A simple type object or invalid if type is not supported.
814
+ ' ----------------------------------------------------------------
815
+ function IG_GetSimpleType(typeStr as string) as object
816
+ item = invalid
817
+
818
+ if typeStr = "integer" or typeStr = "int" or typeStr = "roint"
819
+ item = IG_GetInteger()
820
+ else if typeStr = "float" or typeStr = "rofloat"
821
+ item = IG_GetFloat()
822
+ else if typeStr = "string" or typeStr = "rostring"
823
+ item = IG_GetString(10)
824
+ else if typeStr = "boolean" or typeStr = "roboolean"
825
+ item = IG_GetBoolean()
826
+ end if
827
+
828
+ return item
829
+ end function
830
+
831
+ ' ----------------------------------------------------------------
832
+ ' Generates random boolean value.
833
+
834
+ ' @return A random boolean value.
835
+ ' ----------------------------------------------------------------
836
+ function IG_GetBoolean() as boolean
837
+ return TF_Utils__AsBoolean(Rnd(2) \ Rnd(2))
838
+ end function
839
+
840
+ ' ----------------------------------------------------------------
841
+ ' Generates random integer value from 1 to specified seed value.
842
+
843
+ ' @param seed (integer) A seed value for Rnd function.
844
+ ' Default value: 100.
845
+
846
+ ' @return A random integer value.
847
+ ' ----------------------------------------------------------------
848
+ function IG_GetInteger(seed = 100 as integer) as integer
849
+ return Rnd(seed)
850
+ end function
851
+
852
+ ' ----------------------------------------------------------------
853
+ ' Generates random float value.
854
+
855
+ ' @return A random float value.
856
+ ' ----------------------------------------------------------------
857
+ function IG_GetFloat() as float
858
+ return Rnd(0)
859
+ end function
860
+
861
+ ' ----------------------------------------------------------------
862
+ ' Generates random string with specified length.
863
+
864
+ ' @param seed (integer) A string length.
865
+
866
+ ' @return A random string value or empty string if seed is 0.
867
+ ' ----------------------------------------------------------------
868
+ function IG_GetString(seed as integer) as string
869
+ item = ""
870
+ if seed > 0
871
+ stringLength = Rnd(seed)
872
+
873
+ for i = 0 to stringLength
874
+ chType = Rnd(3)
875
+
876
+ if chType = 1 ' Chr(48-57) - numbers
877
+ chNumber = 47 + Rnd(10)
878
+ else if chType = 2 ' Chr(65-90) - Uppercase Letters
879
+ chNumber = 64 + Rnd(26)
880
+ else ' Chr(97-122) - Lowercase Letters
881
+ chNumber = 96 + Rnd(26)
882
+ end if
883
+
884
+ item = item + Chr(chNumber)
885
+ end for
886
+ end if
887
+
888
+ return item
889
+ end function
890
+ '*****************************************************************
891
+ '* Copyright Roku 2011-2019
892
+ '* All Rights Reserved
893
+ '*****************************************************************
894
+
895
+ ' Functions in this file:
896
+ ' Logger
897
+ ' Logger__SetVerbosity
898
+ ' Logger__SetEcho
899
+ ' Logger__SetServerURL
900
+ ' Logger__PrintStatistic
901
+ ' Logger__SendToServer
902
+ ' Logger__CreateTotalStatistic
903
+ ' Logger__CreateSuiteStatistic
904
+ ' Logger__CreateTestStatistic
905
+ ' Logger__AppendSuiteStatistic
906
+ ' Logger__AppendTestStatistic
907
+ ' Logger__PrintSuiteStatistic
908
+ ' Logger__PrintTestStatistic
909
+ ' Logger__PrintStart
910
+ ' Logger__PrintEnd
911
+ ' Logger__PrintSuiteSetUp
912
+ ' Logger__PrintSuiteStart
913
+ ' Logger__PrintSuiteEnd
914
+ ' Logger__PrintSuiteTearDown
915
+ ' Logger__PrintTestSetUp
916
+ ' Logger__PrintTestStart
917
+ ' Logger__PrintTestEnd
918
+ ' Logger__PrintTestTearDown
919
+
920
+ ' ----------------------------------------------------------------
921
+ ' Main function. Create Logger object.
922
+
923
+ ' @return A Logger object.
924
+ ' ----------------------------------------------------------------
925
+ function Logger() as object
926
+ this = {}
927
+
928
+ this.verbosityLevel = {
929
+ basic: 0
930
+ normal: 1
931
+ verboseFailed: 2
932
+ verbose: 3
933
+ }
934
+
935
+ ' Internal properties
936
+ this.verbosity = this.verbosityLevel.normal
937
+ this.echoEnabled = false
938
+ this.serverURL = ""
939
+ this.jUnitEnabled = false
940
+
941
+ ' Interface
942
+ this.SetVerbosity = Logger__SetVerbosity
943
+ this.SetEcho = Logger__SetEcho
944
+ this.SetJUnit = Logger__SetJUnit
945
+ this.SetServer = Logger__SetServer
946
+ this.SetServerURL = Logger__SetServerURL ' Deprecated. Use Logger__SetServer instead.
947
+ this.PrintStatistic = Logger__PrintStatistic
948
+ this.SendToServer = Logger__SendToServer
949
+
950
+ this.CreateTotalStatistic = Logger__CreateTotalStatistic
951
+ this.CreateSuiteStatistic = Logger__CreateSuiteStatistic
952
+ this.CreateTestStatistic = Logger__CreateTestStatistic
953
+ this.AppendSuiteStatistic = Logger__AppendSuiteStatistic
954
+ this.AppendTestStatistic = Logger__AppendTestStatistic
955
+
956
+ ' Internal functions
957
+ this.PrintSuiteStatistic = Logger__PrintSuiteStatistic
958
+ this.PrintTestStatistic = Logger__PrintTestStatistic
959
+ this.PrintStart = Logger__PrintStart
960
+ this.PrintEnd = Logger__PrintEnd
961
+ this.PrintSuiteSetUp = Logger__PrintSuiteSetUp
962
+ this.PrintSuiteStart = Logger__PrintSuiteStart
963
+ this.PrintSuiteEnd = Logger__PrintSuiteEnd
964
+ this.PrintSuiteTearDown = Logger__PrintSuiteTearDown
965
+ this.PrintTestSetUp = Logger__PrintTestSetUp
966
+ this.PrintTestStart = Logger__PrintTestStart
967
+ this.PrintTestEnd = Logger__PrintTestEnd
968
+ this.PrintTestTearDown = Logger__PrintTestTearDown
969
+ this.PrintJUnitFormat = Logger__PrintJUnitFormat
970
+
971
+ return this
972
+ end function
973
+
974
+ ' ----------------------------------------------------------------
975
+ ' Set logging verbosity parameter.
976
+
977
+ ' @param verbosity (integer) A verbosity level.
978
+ ' Posible values:
979
+ ' 0 - basic
980
+ ' 1 - normal
981
+ ' 2 - verbose failed tests
982
+ ' 3 - verbose
983
+ ' Default level: 1
984
+ ' ----------------------------------------------------------------
985
+ sub Logger__SetVerbosity(verbosity = m.verbosityLevel.normal as integer)
986
+ if verbosity >= m.verbosityLevel.basic and verbosity <= m.verbosityLevel.verbose
987
+ m.verbosity = verbosity
988
+ end if
989
+ end sub
990
+
991
+ ' ----------------------------------------------------------------
992
+ ' Set logging echo parameter.
993
+
994
+ ' @param enable (boolean) A echo trigger.
995
+ ' Posible values: true or false
996
+ ' Default value: false
997
+ ' ----------------------------------------------------------------
998
+ sub Logger__SetEcho(enable = false as boolean)
999
+ m.echoEnabled = enable
1000
+ end sub
1001
+
1002
+ ' ----------------------------------------------------------------
1003
+ ' Set logging JUnit output parameter.
1004
+
1005
+ ' @param enable (boolean) A JUnit output trigger.
1006
+ ' Posible values: true or false
1007
+ ' Default value: false
1008
+ ' ----------------------------------------------------------------
1009
+ sub Logger__SetJUnit(enable = false as boolean)
1010
+ m.jUnitEnabled = enable
1011
+ end sub
1012
+
1013
+ ' ----------------------------------------------------------------
1014
+ ' Set storage server parameters.
1015
+
1016
+ ' @param url (string) Storage server host.
1017
+ ' Default value: ""
1018
+ ' @param port (string) Storage server port.
1019
+ ' Default value: ""
1020
+ ' ----------------------------------------------------------------
1021
+ sub Logger__SetServer(host = "" as string, port = "" as string)
1022
+ if TF_Utils__IsNotEmptyString(host)
1023
+ if TF_Utils__IsNotEmptyString(port)
1024
+ m.serverURL = "http://" + host + ":" + port
1025
+ else
1026
+ m.serverURL = "http://" + host
1027
+ end if
1028
+ end if
1029
+ end sub
1030
+
1031
+ ' ----------------------------------------------------------------
1032
+ ' Set storage server URL parameter.
1033
+
1034
+ ' @param url (string) A storage server URL.
1035
+ ' Default value: ""
1036
+ ' ----------------------------------------------------------------
1037
+ sub Logger__SetServerURL(url = "" as string)
1038
+ ? "This function is deprecated. Please use Logger__SetServer(host, port)"
1039
+ end sub
1040
+
1041
+ '----------------------------------------------------------------
1042
+ ' Send test results as a POST json payload.
1043
+ '
1044
+ ' @param statObj (object) stats of the test run.
1045
+ ' Default value: invalid
1046
+ ' ----------------------------------------------------------------
1047
+ sub Logger__SendToServer(statObj as object)
1048
+ if TF_Utils__IsNotEmptyString(m.serverURL) and TF_Utils__IsValid(statObj)
1049
+ ? "***"
1050
+ ? "*** Sending statsObj to server: "; m.serverURL
1051
+
1052
+ request = CreateObject("roUrlTransfer")
1053
+ request.SetUrl(m.serverURL)
1054
+ statString = FormatJson(statObj)
1055
+
1056
+ ? "*** Response: "; request.postFromString(statString)
1057
+ ? "***"
1058
+ ? "******************************************************************"
1059
+ end if
1060
+ end sub
1061
+
1062
+ ' ----------------------------------------------------------------
1063
+ ' Print statistic object with specified verbosity.
1064
+
1065
+ ' @param statObj (object) A statistic object to print.
1066
+ ' ----------------------------------------------------------------
1067
+ sub Logger__PrintStatistic(statObj as object)
1068
+ if not m.echoEnabled
1069
+ m.PrintStart()
1070
+
1071
+ if m.verbosity = m.verbosityLevel.normal or m.verbosity = m.verbosityLevel.verboseFailed
1072
+ for each testSuite in statObj.Suites
1073
+ for each testCase in testSuite.Tests
1074
+ if m.verbosity = m.verbosityLevel.verboseFailed and testCase.result = "Fail"
1075
+ m.printTestStatistic(testCase)
1076
+ else
1077
+ ? "*** "; testSuite.Name; ": "; testCase.Name; " - "; testCase.Result
1078
+ end if
1079
+ end for
1080
+ end for
1081
+ else if m.verbosity = m.verbosityLevel.verbose
1082
+ for each testSuite in statObj.Suites
1083
+ m.PrintSuiteStatistic(testSuite)
1084
+ end for
1085
+ end if
1086
+ end if
1087
+
1088
+ ? "***"
1089
+ ? "*** Total = "; TF_Utils__AsString(statObj.Total); " ; Passed = "; statObj.Correct; " ; Failed = "; statObj.Fail; " ; Skipped = "; statObj.skipped; " ; Crashes = "; statObj.Crash;
1090
+ ? "*** Time spent: "; statObj.Time; "ms"
1091
+ ? "***"
1092
+
1093
+ m.PrintEnd()
1094
+
1095
+ m.SendToServer(statObj)
1096
+
1097
+ if m.jUnitEnabled
1098
+ m.printJUnitFormat(statObj)
1099
+ end if
1100
+ end sub
1101
+
1102
+ ' ----------------------------------------------------------------
1103
+ ' Create an empty statistic object for totals in output log.
1104
+
1105
+ ' @return An empty statistic object.
1106
+ ' ----------------------------------------------------------------
1107
+ function Logger__CreateTotalStatistic() as object
1108
+ statTotalItem = {
1109
+ Suites: []
1110
+ Time: 0
1111
+ Total: 0
1112
+ Correct: 0
1113
+ Fail: 0
1114
+ Skipped: 0
1115
+ Crash: 0
1116
+ }
1117
+
1118
+ if m.echoEnabled
1119
+ m.PrintStart()
1120
+ end if
1121
+
1122
+ return statTotalItem
1123
+ end function
1124
+
1125
+ ' ----------------------------------------------------------------
1126
+ ' Create an empty statistic object for test suite with specified name.
1127
+
1128
+ ' @param name (string) A test suite name for statistic object.
1129
+
1130
+ ' @return An empty statistic object for test suite.
1131
+ ' ----------------------------------------------------------------
1132
+ function Logger__CreateSuiteStatistic(name as string) as object
1133
+ statSuiteItem = {
1134
+ Name: name
1135
+ Tests: []
1136
+ Time: 0
1137
+ Total: 0
1138
+ Correct: 0
1139
+ Fail: 0
1140
+ Skipped: 0
1141
+ Crash: 0
1142
+ }
1143
+
1144
+ if m.echoEnabled
1145
+ if m.verbosity = m.verbosityLevel.verbose
1146
+ m.PrintSuiteStart(name)
1147
+ end if
1148
+ end if
1149
+
1150
+ return statSuiteItem
1151
+ end function
1152
+
1153
+ ' ----------------------------------------------------------------
1154
+ ' Create statistic object for test with specified name.
1155
+
1156
+ ' @param name (string) A test name.
1157
+ ' @param result (string) A result of test running.
1158
+ ' Posible values: "Success", "Fail".
1159
+ ' Default value: "Success"
1160
+ ' @param time (integer) A test running time.
1161
+ ' Default value: 0
1162
+ ' @param errorCode (integer) An error code for failed test.
1163
+ ' Posible values:
1164
+ ' 252 (&hFC) : ERR_NORMAL_END
1165
+ ' 226 (&hE2) : ERR_VALUE_RETURN
1166
+ ' 233 (&hE9) : ERR_USE_OF_UNINIT_VAR
1167
+ ' 020 (&h14) : ERR_DIV_ZERO
1168
+ ' 024 (&h18) : ERR_TM
1169
+ ' 244 (&hF4) : ERR_RO2
1170
+ ' 236 (&hEC) : ERR_RO4
1171
+ ' 002 (&h02) : ERR_SYNTAX
1172
+ ' 241 (&hF1) : ERR_WRONG_NUM_PARAM
1173
+ ' Default value: 0
1174
+ ' @param errorMessage (string) An error message for failed test.
1175
+
1176
+ ' @return A statistic object for test.
1177
+ ' ----------------------------------------------------------------
1178
+ function Logger__CreateTestStatistic(name as string, result = "Success" as string, time = 0 as integer, errorCode = 0 as integer, errorMessage = "" as string, isInit = false as boolean) as object
1179
+ statTestItem = {
1180
+ Name: name
1181
+ Result: result
1182
+ Time: time
1183
+ PerfData: {}
1184
+ Error: {
1185
+ Code: errorCode
1186
+ Message: errorMessage
1187
+ }
1188
+ }
1189
+
1190
+ if m.echoEnabled and not isInit
1191
+ if m.verbosity = m.verbosityLevel.verbose
1192
+ m.PrintTestStart(name)
1193
+ end if
1194
+ end if
1195
+
1196
+ return statTestItem
1197
+ end function
1198
+
1199
+ ' ----------------------------------------------------------------
1200
+ ' Append test statistic to test suite statistic.
1201
+
1202
+ ' @param statSuiteObj (object) A target test suite object.
1203
+ ' @param statTestObj (object) A test statistic to append.
1204
+ ' ----------------------------------------------------------------
1205
+ sub Logger__AppendTestStatistic(statSuiteObj as object, statTestObj as object)
1206
+ if TF_Utils__IsAssociativeArray(statSuiteObj) and TF_Utils__IsAssociativeArray(statTestObj)
1207
+ statSuiteObj.Tests.Push(statTestObj)
1208
+
1209
+ if TF_Utils__IsInteger(statTestObj.time)
1210
+ statSuiteObj.Time = statSuiteObj.Time + statTestObj.Time
1211
+ end if
1212
+
1213
+ statSuiteObj.Total = statSuiteObj.Total + 1
1214
+
1215
+ if LCase(statTestObj.Result) = "success"
1216
+ statSuiteObj.Correct = statSuiteObj.Correct + 1
1217
+ else if LCase(statTestObj.result) = "fail"
1218
+ statSuiteObj.Fail = statSuiteObj.Fail + 1
1219
+ else if LCase(statTestObj.result) = "skipped"
1220
+ statSuiteObj.skipped++
1221
+ else
1222
+ statSuiteObj.crash = statSuiteObj.crash + 1
1223
+ end if
1224
+
1225
+ if m.echoEnabled
1226
+ if m.verbosity = m.verbosityLevel.normal
1227
+ ? "*** "; statSuiteObj.Name; ": "; statTestObj.Name; " - "; statTestObj.Result
1228
+ else if m.verbosity = m.verbosityLevel.verbose
1229
+ m.PrintTestStatistic(statTestObj)
1230
+ end if
1231
+ end if
1232
+ end if
1233
+ end sub
1234
+
1235
+ ' ----------------------------------------------------------------
1236
+ ' Append suite statistic to total statistic object.
1237
+
1238
+ ' @param statTotalObj (object) A target total statistic object.
1239
+ ' @param statSuiteObj (object) A test suite statistic object to append.
1240
+ ' ----------------------------------------------------------------
1241
+ sub Logger__AppendSuiteStatistic(statTotalObj as object, statSuiteObj as object)
1242
+ if TF_Utils__IsAssociativeArray(statTotalObj) and TF_Utils__IsAssociativeArray(statSuiteObj)
1243
+ statTotalObj.Suites.Push(statSuiteObj)
1244
+ statTotalObj.Time = statTotalObj.Time + statSuiteObj.Time
1245
+
1246
+ if TF_Utils__IsInteger(statSuiteObj.Total)
1247
+ statTotalObj.Total = statTotalObj.Total + statSuiteObj.Total
1248
+ end if
1249
+
1250
+ if TF_Utils__IsInteger(statSuiteObj.Correct)
1251
+ statTotalObj.Correct = statTotalObj.Correct + statSuiteObj.Correct
1252
+ end if
1253
+
1254
+ if TF_Utils__IsInteger(statSuiteObj.Fail)
1255
+ statTotalObj.Fail = statTotalObj.Fail + statSuiteObj.Fail
1256
+ end if
1257
+
1258
+ if TF_Utils__IsInteger(statSuiteObj.skipped)
1259
+ statTotalObj.skipped += statSuiteObj.skipped
1260
+ end if
1261
+
1262
+ if TF_Utils__IsInteger(statSuiteObj.Crash)
1263
+ statTotalObj.Crash = statTotalObj.Crash + statSuiteObj.Crash
1264
+ end if
1265
+
1266
+ if m.echoEnabled
1267
+ if m.verbosity = m.verbosityLevel.verbose
1268
+ m.PrintSuiteStatistic(statSuiteObj)
1269
+ end if
1270
+ end if
1271
+ end if
1272
+ end sub
1273
+
1274
+ ' ----------------------------------------------------------------
1275
+ ' Print test suite statistic.
1276
+
1277
+ ' @param statSuiteObj (object) A target test suite object to print.
1278
+ ' ----------------------------------------------------------------
1279
+ sub Logger__PrintSuiteStatistic(statSuiteObj as object)
1280
+ if not m.echoEnabled
1281
+ m.PrintSuiteStart(statSuiteObj.Name)
1282
+
1283
+ for each testCase in statSuiteObj.Tests
1284
+ m.PrintTestStatistic(testCase)
1285
+ end for
1286
+ end if
1287
+
1288
+ ? "==="
1289
+ ? "=== Total = "; TF_Utils__AsString(statSuiteObj.Total); " ; Passed = "; statSuiteObj.Correct; " ; Failed = "; statSuiteObj.Fail; " ; Skipped = "; statSuiteObj.skipped; " ; Crashes = "; statSuiteObj.Crash;
1290
+ ? " Time spent: "; statSuiteObj.Time; "ms"
1291
+ ? "==="
1292
+
1293
+ m.PrintSuiteEnd(statSuiteObj.Name)
1294
+ end sub
1295
+
1296
+ ' ----------------------------------------------------------------
1297
+ ' Print test statistic.
1298
+
1299
+ ' @param statTestObj (object) A target test object to print.
1300
+ ' ----------------------------------------------------------------
1301
+ sub Logger__PrintTestStatistic(statTestObj as object)
1302
+ if not m.echoEnabled
1303
+ m.PrintTestStart(statTestObj.Name)
1304
+ end if
1305
+
1306
+ ? "--- Result: "; statTestObj.Result
1307
+ ? "--- Time: "; statTestObj.Time
1308
+
1309
+ if LCase(statTestObj.result) = "skipped"
1310
+ if Len(statTestObj.message) > 0
1311
+ ? "--- Message: "; statTestObj.message
1312
+ end if
1313
+ else if LCase(statTestObj.Result) <> "success"
1314
+ ? "--- Error Code: "; statTestObj.Error.Code
1315
+ ? "--- Error Message: "; statTestObj.Error.Message
1316
+ end if
1317
+
1318
+ m.PrintTestEnd(statTestObj.Name)
1319
+ end sub
1320
+
1321
+ ' ----------------------------------------------------------------
1322
+ ' Print testting start message.
1323
+ ' ----------------------------------------------------------------
1324
+ sub Logger__PrintStart()
1325
+ ? ""
1326
+ ? "******************************************************************"
1327
+ ? "******************************************************************"
1328
+ ? "************* Start testing *************"
1329
+ ? "******************************************************************"
1330
+ end sub
1331
+
1332
+ ' ----------------------------------------------------------------
1333
+ ' Print testing end message.
1334
+ ' ----------------------------------------------------------------
1335
+ sub Logger__PrintEnd()
1336
+ ? "******************************************************************"
1337
+ ? "************* End testing *************"
1338
+ ? "******************************************************************"
1339
+ ? "******************************************************************"
1340
+ ? ""
1341
+ end sub
1342
+
1343
+ ' ----------------------------------------------------------------
1344
+ ' Print test suite SetUp message.
1345
+ ' ----------------------------------------------------------------
1346
+ sub Logger__PrintSuiteSetUp(sName as string)
1347
+ if m.verbosity = m.verbosityLevel.verbose
1348
+ ? "================================================================="
1349
+ ? "=== SetUp "; sName; " suite."
1350
+ ? "================================================================="
1351
+ end if
1352
+ end sub
1353
+
1354
+ ' ----------------------------------------------------------------
1355
+ ' Print test suite start message.
1356
+ ' ----------------------------------------------------------------
1357
+ sub Logger__PrintSuiteStart(sName as string)
1358
+ ? "================================================================="
1359
+ ? "=== Start "; sName; " suite:"
1360
+ ? "==="
1361
+ end sub
1362
+
1363
+ ' ----------------------------------------------------------------
1364
+ ' Print test suite end message.
1365
+ ' ----------------------------------------------------------------
1366
+ sub Logger__PrintSuiteEnd(sName as string)
1367
+ ? "==="
1368
+ ? "=== End "; sName; " suite."
1369
+ ? "================================================================="
1370
+ end sub
1371
+
1372
+ ' ----------------------------------------------------------------
1373
+ ' Print test suite TearDown message.
1374
+ ' ----------------------------------------------------------------
1375
+ sub Logger__PrintSuiteTearDown(sName as string)
1376
+ if m.verbosity = m.verbosityLevel.verbose
1377
+ ? "================================================================="
1378
+ ? "=== TearDown "; sName; " suite."
1379
+ ? "================================================================="
1380
+ end if
1381
+ end sub
1382
+
1383
+ ' ----------------------------------------------------------------
1384
+ ' Print test setUp message.
1385
+ ' ----------------------------------------------------------------
1386
+ sub Logger__PrintTestSetUp(tName as string)
1387
+ if m.verbosity = m.verbosityLevel.verbose
1388
+ ? "----------------------------------------------------------------"
1389
+ ? "--- SetUp "; tName; " test."
1390
+ ? "----------------------------------------------------------------"
1391
+ end if
1392
+ end sub
1393
+
1394
+ ' ----------------------------------------------------------------
1395
+ ' Print test start message.
1396
+ ' ----------------------------------------------------------------
1397
+ sub Logger__PrintTestStart(tName as string)
1398
+ ? "----------------------------------------------------------------"
1399
+ ? "--- Start "; tName; " test:"
1400
+ ? "---"
1401
+ end sub
1402
+
1403
+ ' ----------------------------------------------------------------
1404
+ ' Print test end message.
1405
+ ' ----------------------------------------------------------------
1406
+ sub Logger__PrintTestEnd(tName as string)
1407
+ ? "---"
1408
+ ? "--- End "; tName; " test."
1409
+ ? "----------------------------------------------------------------"
1410
+ end sub
1411
+
1412
+ ' ----------------------------------------------------------------
1413
+ ' Print test TearDown message.
1414
+ ' ----------------------------------------------------------------
1415
+ sub Logger__PrintTestTearDown(tName as string)
1416
+ if m.verbosity = m.verbosityLevel.verbose
1417
+ ? "----------------------------------------------------------------"
1418
+ ? "--- TearDown "; tName; " test."
1419
+ ? "----------------------------------------------------------------"
1420
+ end if
1421
+ end sub
1422
+
1423
+ sub Logger__PrintJUnitFormat(statObj as object)
1424
+ ' TODO finish report
1425
+ xml = CreateObject("roXMLElement")
1426
+ xml.SetName("testsuites")
1427
+ for each testSuiteAA in statObj.suites
1428
+ testSuite = xml.AddElement("testsuite")
1429
+ ' name="FeatureManagerTest" time="13.923" tests="2" errors="0" skipped="0" failures="0"
1430
+ testSuite.AddAttribute("name", testSuiteAA.name)
1431
+ testSuite.AddAttribute("time", testSuiteAA.time.toStr())
1432
+ testSuite.AddAttribute("tests", testSuiteAA.Tests.count().toStr())
1433
+
1434
+ skippedNum = 0
1435
+ failedNum = 0
1436
+ for each testAA in testSuiteAA.Tests
1437
+ test = testSuite.AddElement("testcase")
1438
+ test.AddAttribute("name", testAA.name)
1439
+ test.AddAttribute("time", testAA.time.toStr())
1440
+
1441
+ if LCase(testAA.result) = "skipped" then
1442
+ test.AddElement("skipped")
1443
+ skippedNum++
1444
+ else if LCase(testAA.Result) <> "success"
1445
+ failure = test.AddElement("failure")
1446
+ failure.AddAttribute("message", testAA.error.message)
1447
+ failure.AddAttribute("type", testAA.error.code.tostr())
1448
+ failedNum++
1449
+ end if
1450
+ end for
1451
+ testSuite.AddAttribute("errors", failedNum.tostr())
1452
+ testSuite.AddAttribute("skipped", skippedNum.tostr())
1453
+ end for
1454
+ ? xml.GenXML(true)
1455
+ end sub
1456
+ '*****************************************************************
1457
+ '* Copyright Roku 2011-2019
1458
+ '* All Rights Reserved
1459
+ '*****************************************************************
1460
+
1461
+ ' Functions in this file:
1462
+ ' TestRunner
1463
+ ' TestRunner__Run
1464
+ ' TestRunner__SetTestsDirectory
1465
+ ' TestRunner__SetTestFilePrefix
1466
+ ' TestRunner__SetTestSuitePrefix
1467
+ ' TestRunner__SetTestSuiteName
1468
+ ' TestRunner__SetTestCaseName
1469
+ ' TestRunner__SetFailFast
1470
+ ' TestRunner__GetTestSuitesList
1471
+ ' TestRunner__GetTestSuiteNamesList
1472
+ ' TestRunner__GetTestFilesList
1473
+ ' TestRunner__GetTestNodesList
1474
+ ' TestFramework__RunNodeTests
1475
+
1476
+ ' ----------------------------------------------------------------
1477
+ ' Main function. Create TestRunner object.
1478
+
1479
+ ' @return A TestRunner object.
1480
+ ' ----------------------------------------------------------------
1481
+ function TestRunner() as object
1482
+ this = {}
1483
+ GetGlobalAA().globalErrorsList = []
1484
+ this.isNodeMode = GetGlobalAA().top <> invalid
1485
+ this.Logger = Logger()
1486
+
1487
+ ' Internal properties
1488
+ this.SKIP_TEST_MESSAGE_PREFIX = "SKIP_TEST_MESSAGE_PREFIX__"
1489
+ this.nodesTestDirectory = "pkg:/components/tests"
1490
+ if this.isNodeMode
1491
+ this.testsDirectory = this.nodesTestDirectory
1492
+ this.testFilePrefix = m.top.subtype()
1493
+ else
1494
+ this.testsDirectory = "pkg:/source/tests"
1495
+ this.testFilePrefix = "Test__"
1496
+ end if
1497
+ this.testSuitePrefix = "TestSuite__"
1498
+ this.testSuiteName = ""
1499
+ this.testCaseName = ""
1500
+ this.failFast = false
1501
+
1502
+ ' Interface
1503
+ this.Run = TestRunner__Run
1504
+ this.SetTestsDirectory = TestRunner__SetTestsDirectory
1505
+ this.SetTestFilePrefix = TestRunner__SetTestFilePrefix
1506
+ this.SetTestSuitePrefix = TestRunner__SetTestSuitePrefix
1507
+ this.SetTestSuiteName = TestRunner__SetTestSuiteName ' Obsolete, will be removed in next versions
1508
+ this.SetTestCaseName = TestRunner__SetTestCaseName ' Obsolete, will be removed in next versions
1509
+ this.SetFailFast = TestRunner__SetFailFast
1510
+ this.SetFunctions = TestRunner__SetFunctions
1511
+ this.SetIncludeFilter = TestRunner__SetIncludeFilter
1512
+ this.SetExcludeFilter = TestRunner__SetExcludeFilter
1513
+
1514
+ ' Internal functions
1515
+ this.GetTestFilesList = TestRunner__GetTestFilesList
1516
+ this.GetTestSuitesList = TestRunner__GetTestSuitesList
1517
+ this.GetTestNodesList = TestRunner__GetTestNodesList
1518
+ this.GetTestSuiteNamesList = TestRunner__GetTestSuiteNamesList
1519
+ this.GetIncludeFilter = TestRunner__GetIncludeFilter
1520
+ this.GetExcludeFilter = TestRunner__GetExcludeFilter
1521
+
1522
+ return this
1523
+ end function
1524
+
1525
+ ' ----------------------------------------------------------------
1526
+ ' Run main test loop.
1527
+
1528
+ ' @param statObj (object, optional) statistic object to be used in tests
1529
+ ' @param testSuiteNamesList (array, optional) array of test suite function names to be used in tests
1530
+
1531
+ ' @return Statistic object if run in node mode, invalid otherwise
1532
+ ' ----------------------------------------------------------------
1533
+ function TestRunner__Run(statObj = m.Logger.CreateTotalStatistic() as object, testSuiteNamesList = [] as object) as object
1534
+ alltestCount = 0
1535
+ totalStatObj = statObj
1536
+ testSuitesList = m.GetTestSuitesList(testSuiteNamesList)
1537
+
1538
+ globalErrorsList = GetGlobalAA().globalErrorsList
1539
+ for each testSuite in testSuitesList
1540
+ testCases = testSuite.testCases
1541
+ testCount = testCases.Count()
1542
+ alltestCount = alltestCount + testCount
1543
+
1544
+ IS_NEW_APPROACH = testSuite.IS_NEW_APPROACH
1545
+ ' create dedicated env for each test, so that they will have not global m and don't rely on m.that is set in another suite
1546
+ env = {}
1547
+
1548
+ if TF_Utils__IsFunction(testSuite.SetUp)
1549
+ m.Logger.PrintSuiteSetUp(testSuite.Name)
1550
+ if IS_NEW_APPROACH then
1551
+ env.functionToCall = testSuite.SetUp
1552
+ env.functionToCall()
1553
+ else
1554
+ testSuite.SetUp()
1555
+ end if
1556
+ end if
1557
+
1558
+ suiteStatObj = m.Logger.CreateSuiteStatistic(testSuite.Name)
1559
+ ' Initiate empty test statistics object to print results if no tests was run
1560
+ testStatObj = m.Logger.CreateTestStatistic("", "Success", 0, 0, "", true)
1561
+ for each testCase in testCases
1562
+ ' clear all existing errors
1563
+ globalErrorsList.clear()
1564
+
1565
+ if m.testCaseName = "" or (m.testCaseName <> "" and LCase(testCase.Name) = LCase(m.testCaseName))
1566
+ skipTest = TF_Utils__AsBoolean(testCase.skip)
1567
+
1568
+ if TF_Utils__IsFunction(testCase.SetUp) and not skipTest
1569
+ m.Logger.PrintTestSetUp(testCase.Name)
1570
+ if IS_NEW_APPROACH then
1571
+ env.functionToCall = testCase.SetUp
1572
+ env.functionToCall()
1573
+ else
1574
+ testCase.SetUp()
1575
+ end if
1576
+ end if
1577
+
1578
+ testTimer = CreateObject("roTimespan")
1579
+ testStatObj = m.Logger.CreateTestStatistic(testCase.Name)
1580
+
1581
+ if skipTest
1582
+ runResult = m.SKIP_TEST_MESSAGE_PREFIX + "Test was skipped according to specified filters"
1583
+ else
1584
+ testSuite.testInstance = testCase
1585
+ testSuite.testCase = testCase.Func
1586
+
1587
+ runResult = ""
1588
+ if IS_NEW_APPROACH then
1589
+ env.functionToCall = testCase.Func
1590
+
1591
+ if GetInterface(env.functionToCall, "ifFunction") <> invalid
1592
+ if testCase.hasArguments then
1593
+ env.functionToCall(testCase.arg)
1594
+ else
1595
+ env.functionToCall()
1596
+ end if
1597
+ else
1598
+ UTF_fail("Failed to execute test """ + testCase.Name + """ function pointer not found")
1599
+ end if
1600
+ else
1601
+ runResult = testSuite.testCase()
1602
+ end if
1603
+ end if
1604
+
1605
+ if TF_Utils__IsFunction(testCase.TearDown) and not skipTest
1606
+ m.Logger.PrintTestTearDown(testCase.Name)
1607
+ if IS_NEW_APPROACH then
1608
+ env.functionToCall = testCase.TearDown
1609
+ env.functionToCall()
1610
+ else
1611
+ testCase.TearDown()
1612
+ end if
1613
+ end if
1614
+
1615
+ if IS_NEW_APPROACH then
1616
+ if globalErrorsList.count() > 0
1617
+ for each error in globalErrorsList
1618
+ runResult += error + Chr(10) + string(10, "-") + Chr(10)
1619
+ end for
1620
+ end if
1621
+ end if
1622
+
1623
+ if runResult <> ""
1624
+ if InStr(0, runResult, m.SKIP_TEST_MESSAGE_PREFIX) = 1
1625
+ testStatObj.result = "Skipped"
1626
+ testStatObj.message = runResult.Mid(Len(m.SKIP_TEST_MESSAGE_PREFIX)) ' remove prefix from the message
1627
+ else
1628
+ testStatObj.Result = "Fail"
1629
+ testStatObj.Error.Code = 1
1630
+ testStatObj.Error.Message = runResult
1631
+ end if
1632
+ else
1633
+ testStatObj.Result = "Success"
1634
+ end if
1635
+
1636
+ testStatObj.Time = testTimer.TotalMilliseconds()
1637
+ m.Logger.AppendTestStatistic(suiteStatObj, testStatObj)
1638
+
1639
+ if testStatObj.Result = "Fail" and m.failFast
1640
+ suiteStatObj.Result = "Fail"
1641
+ exit for
1642
+ end if
1643
+ end if
1644
+ end for
1645
+
1646
+ m.Logger.AppendSuiteStatistic(totalStatObj, suiteStatObj)
1647
+
1648
+ if TF_Utils__IsFunction(testSuite.TearDown)
1649
+ m.Logger.PrintSuiteTearDown(testSuite.Name)
1650
+ testSuite.TearDown()
1651
+ end if
1652
+
1653
+ if suiteStatObj.Result = "Fail" and m.failFast
1654
+ exit for
1655
+ end if
1656
+ end for
1657
+
1658
+ gthis = GetGlobalAA()
1659
+ msg = ""
1660
+ if gthis.notFoundFunctionPointerList <> invalid then
1661
+ msg = Chr(10) + string(40, "---") + Chr(10)
1662
+ if m.isNodeMode
1663
+ fileNamesString = ""
1664
+
1665
+ for each testSuiteObject in testSuiteNamesList
1666
+ if GetInterface(testSuiteObject, "ifString") <> invalid then
1667
+ fileNamesString += testSuiteObject + ".brs, "
1668
+ else if GetInterface(testSuiteObject, "ifAssociativeArray") <> invalid then
1669
+ if testSuiteObject.filePath <> invalid then
1670
+ fileNamesString += testSuiteObject.filePath + ", "
1671
+ end if
1672
+ end if
1673
+ end for
1674
+
1675
+ msg += Chr(10) + "Create this function below in one of these files"
1676
+ msg += Chr(10) + fileNamesString + Chr(10)
1677
+
1678
+ msg += Chr(10) + "sub init()"
1679
+ end if
1680
+ msg += Chr(10) + "Runner.SetFunctions([" + Chr(10) + " testCase" + Chr(10) + "])"
1681
+ msg += Chr(10) + "For example we think this might resolve your issue"
1682
+ msg += Chr(10) + "Runner = TestRunner()"
1683
+ msg += Chr(10) + "Runner.SetFunctions(["
1684
+
1685
+ tmpMap = {}
1686
+ for each functionName in gthis.notFoundFunctionPointerList
1687
+ if tmpMap[functionName] = invalid then
1688
+ tmpMap[functionName] = ""
1689
+ msg += Chr(10) + " " + functionName
1690
+ end if
1691
+ end for
1692
+
1693
+ msg += Chr(10) + "])"
1694
+ if m.isNodeMode then
1695
+ msg += Chr(10) + "end sub"
1696
+ else
1697
+ msg += Chr(10) + "Runner.Run()"
1698
+ end if
1699
+ end if
1700
+
1701
+ if m.isNodeMode
1702
+ if msg.Len() > 0 then
1703
+ if totalStatObj.notFoundFunctionsMessage = invalid then totalStatObj.notFoundFunctionsMessage = ""
1704
+ totalStatObj.notFoundFunctionsMessage += msg
1705
+ end if
1706
+ return totalStatObj
1707
+ else
1708
+ testNodes = m.getTestNodesList()
1709
+ for each testNodeName in testNodes
1710
+ testNode = CreateObject("roSGNode", testNodeName)
1711
+ if testNode <> invalid
1712
+ testSuiteNamesList = m.GetTestSuiteNamesList(testNodeName)
1713
+ if CreateObject("roSGScreen").CreateScene(testNodeName) <> invalid
1714
+ ? "WARNING: Test cases cannot be run in main scene."
1715
+ for each testSuiteName in testSuiteNamesList
1716
+ suiteStatObj = m.Logger.CreateSuiteStatistic(testSuiteName)
1717
+ suiteStatObj.fail = 1
1718
+ suiteStatObj.total = 1
1719
+ m.Logger.AppendSuiteStatistic(totalStatObj, suiteStatObj)
1720
+ end for
1721
+ else
1722
+ params = [m, totalStatObj, testSuiteNamesList, m.GetIncludeFilter(), m.GetExcludeFilter()]
1723
+ tmp = testNode.callFunc("TestFramework__RunNodeTests", params)
1724
+ if tmp <> invalid then
1725
+ totalStatObj = tmp
1726
+ end if
1727
+ end if
1728
+ end if
1729
+ end for
1730
+
1731
+ m.Logger.PrintStatistic(totalStatObj)
1732
+ end if
1733
+
1734
+ if msg.Len() > 0 or totalStatObj.notFoundFunctionsMessage <> invalid then
1735
+ title = ""
1736
+ title += Chr(10) + "NOTE: If some your tests haven't been executed this might be due to outdated list of functions"
1737
+ title += Chr(10) + "To resolve this issue please execute" + Chr(10) + Chr(10)
1738
+
1739
+ title += msg
1740
+
1741
+ if totalStatObj.notFoundFunctionsMessage <> invalid then
1742
+ title += totalStatObj.notFoundFunctionsMessage
1743
+ end if
1744
+ ? title
1745
+ end if
1746
+ end function
1747
+
1748
+ ' ----------------------------------------------------------------
1749
+ ' Set testsDirectory property.
1750
+ ' ----------------------------------------------------------------
1751
+ sub TestRunner__SetTestsDirectory(testsDirectory as string)
1752
+ m.testsDirectory = testsDirectory
1753
+ end sub
1754
+
1755
+ ' ----------------------------------------------------------------
1756
+ ' Set testFilePrefix property.
1757
+ ' ----------------------------------------------------------------
1758
+ sub TestRunner__SetTestFilePrefix(testFilePrefix as string)
1759
+ m.testFilePrefix = testFilePrefix
1760
+ end sub
1761
+
1762
+ ' ----------------------------------------------------------------
1763
+ ' Set testSuitePrefix property.
1764
+ ' ----------------------------------------------------------------
1765
+ sub TestRunner__SetTestSuitePrefix(testSuitePrefix as string)
1766
+ m.testSuitePrefix = testSuitePrefix
1767
+ end sub
1768
+
1769
+ ' ----------------------------------------------------------------
1770
+ ' Set testSuiteName property.
1771
+ ' ----------------------------------------------------------------
1772
+ sub TestRunner__SetTestSuiteName(testSuiteName as string)
1773
+ m.testSuiteName = testSuiteName
1774
+ end sub
1775
+
1776
+ ' ----------------------------------------------------------------
1777
+ ' Set testCaseName property.
1778
+ ' ----------------------------------------------------------------
1779
+ sub TestRunner__SetTestCaseName(testCaseName as string)
1780
+ m.testCaseName = testCaseName
1781
+ end sub
1782
+
1783
+ ' ----------------------------------------------------------------
1784
+ ' Set failFast property.
1785
+ ' ----------------------------------------------------------------
1786
+ sub TestRunner__SetFailFast(failFast = false as boolean)
1787
+ m.failFast = failFast
1788
+ end sub
1789
+
1790
+ ' ----------------------------------------------------------------
1791
+ ' Builds an array of test suite objects.
1792
+
1793
+ ' @param testSuiteNamesList (string, optional) array of names of test suite functions. If not passed, scans all test files for test suites
1794
+
1795
+ ' @return An array of test suites.
1796
+ ' ----------------------------------------------------------------
1797
+ function TestRunner__GetTestSuitesList(testSuiteNamesList = [] as object) as object
1798
+ result = []
1799
+
1800
+ if testSuiteNamesList.count() > 0
1801
+ for each value in testSuiteNamesList
1802
+ if TF_Utils__IsString(value) then
1803
+ tmpTestSuiteFunction = TestFramework__getFunctionPointer(value)
1804
+ if tmpTestSuiteFunction <> invalid then
1805
+ testSuite = tmpTestSuiteFunction()
1806
+
1807
+ if TF_Utils__IsAssociativeArray(testSuite)
1808
+ result.Push(testSuite)
1809
+ end if
1810
+ end if
1811
+ ' also we can get AA that will give source code and filePath
1812
+ ' Please be aware this is executed in render thread
1813
+ else if GetInterface(value, "ifAssociativeArray") <> invalid then
1814
+ ' try to use new approach
1815
+ testSuite = ScanFileForNewTests(value.code, value.filePath)
1816
+ if testSuite <> invalid then
1817
+ result.push(testSuite)
1818
+ end if
1819
+ else if GetInterface(value, "ifFunction") <> invalid then
1820
+ result.Push(value)
1821
+ end if
1822
+ end for
1823
+ else
1824
+ testSuiteRegex = CreateObject("roRegex", "^(function|sub)\s(" + m.testSuitePrefix + m.testSuiteName + "[0-9a-z\_]*)\s*\(", "i")
1825
+ testFilesList = m.GetTestFilesList()
1826
+
1827
+ for each filePath in testFilesList
1828
+ code = TF_Utils__AsString(ReadAsciiFile(filePath))
1829
+
1830
+ if code <> ""
1831
+ foundTestSuite = false
1832
+ for each line in code.Tokenize(Chr(10))
1833
+ line.Trim()
1834
+
1835
+ if testSuiteRegex.IsMatch(line)
1836
+ testSuite = invalid
1837
+ functionName = testSuiteRegex.Match(line).Peek()
1838
+
1839
+ tmpTestSuiteFunction = TestFramework__getFunctionPointer(functionName)
1840
+ if tmpTestSuiteFunction <> invalid then
1841
+ testSuite = tmpTestSuiteFunction()
1842
+
1843
+ if TF_Utils__IsAssociativeArray(testSuite)
1844
+ result.Push(testSuite)
1845
+ foundTestSuite = true
1846
+ else
1847
+ ' TODO check if we need this
1848
+ ' using new mode
1849
+ ' testSuite = ScanFileForNewTests(code, filePath)
1850
+
1851
+ ' exit for
1852
+ end if
1853
+ end if
1854
+ end if
1855
+ end for
1856
+ if not foundTestSuite then
1857
+ testSuite = ScanFileForNewTests(code, filePath)
1858
+ if testSuite <> invalid then
1859
+ result.push(testSuite)
1860
+ end if
1861
+ end if
1862
+ end if
1863
+ end for
1864
+ end if
1865
+
1866
+ return result
1867
+ end function
1868
+
1869
+ function ScanFileForNewTests(souceCode, filePath)
1870
+ foundAnyTest = false
1871
+ testSuite = BaseTestSuite()
1872
+
1873
+ allowedAnnotationsRegex = CreateObject("roRegex", "^'\s*@(test|beforeall|beforeeach|afterall|aftereach|repeatedtest|parameterizedtest|methodsource|ignore)\s*|\n", "i")
1874
+ voidFunctionRegex = CreateObject("roRegex", "^(function|sub)\s([a-z0-9A-Z_]*)\(\)", "i")
1875
+ anyArgsFunctionRegex = CreateObject("roRegex", "^(function|sub)\s([a-z0-9A-Z_]*)\(", "i")
1876
+
1877
+ processors = {
1878
+ testSuite: testSuite
1879
+ filePath: filePath
1880
+ currentLine: ""
1881
+ annotations: {}
1882
+
1883
+ functionName: ""
1884
+
1885
+ tests: []
1886
+
1887
+ beforeEachFunc: invalid
1888
+ beforeAllFunc: invalid
1889
+
1890
+ AfterEachFunc: invalid
1891
+ AfterAllFunc: invalid
1892
+
1893
+ isParameterizedTest: false
1894
+ MethodForArguments: ""
1895
+ executedParametrizedAdding: false
1896
+
1897
+ test: sub()
1898
+ skipTest = m.doSkipTest(m.functionName)
1899
+ funcPointer = m.getFunctionPointer(m.functionName)
1900
+ m.tests.push({ name: m.functionName, pointer: funcPointer, skip: skipTest })
1901
+ end sub
1902
+
1903
+ repeatedtest: sub()
1904
+ allowedAnnotationsRegex = CreateObject("roRegex", "^'\s*@(repeatedtest)\((\d*)\)", "i")
1905
+ annotationLine = m.annotations["repeatedtest"].line
1906
+ if allowedAnnotationsRegex.IsMatch(annotationLine)
1907
+ groups = allowedAnnotationsRegex.Match(annotationLine)
1908
+ numberOfLoops = groups[2]
1909
+ if numberOfLoops <> invalid and TF_Utils__AsInteger(numberOfLoops) > 0 then
1910
+ numberOfLoops = TF_Utils__AsInteger(numberOfLoops)
1911
+ funcPointer = m.getFunctionPointer(m.functionName)
1912
+ for index = 1 to numberOfLoops
1913
+ skipTest = m.doSkipTest(m.functionName)
1914
+ text = " " + index.tostr() + " of " + numberOfLoops.tostr()
1915
+ m.tests.push({ name: m.functionName + text, pointer: funcPointer, skip: skipTest })
1916
+ end for
1917
+ end if
1918
+ else
1919
+ ? "WARNING: Wrong format of repeatedTest(numberOfRuns) "annotationLine
1920
+ end if
1921
+ end sub
1922
+
1923
+ parameterizedTest: sub()
1924
+ m.processParameterizedTests()
1925
+ end sub
1926
+
1927
+ methodSource: sub()
1928
+ m.processParameterizedTests()
1929
+ end sub
1930
+
1931
+ processParameterizedTests: sub()
1932
+ ' add test if it was not added already
1933
+ if not m.executedParametrizedAdding
1934
+ if m.annotations.methodSource <> invalid and m.annotations.parameterizedTest <> invalid then
1935
+ methodAnottation = m.annotations.methodSource.line
1936
+
1937
+ allowedAnnotationsRegex = CreateObject("roRegex", "^'\s*@(methodsource)\(" + Chr(34) + "([A-Za-z0-9_]*)" + Chr(34) + "\)", "i")
1938
+
1939
+ if allowedAnnotationsRegex.IsMatch(methodAnottation)
1940
+ groups = allowedAnnotationsRegex.Match(methodAnottation)
1941
+ providerFunction = groups[2]
1942
+
1943
+ providerFunctionPointer = m.getFunctionPointer(providerFunction)
1944
+
1945
+ if providerFunctionPointer <> invalid then
1946
+ funcPointer = m.getFunctionPointer(m.functionName)
1947
+
1948
+ args = providerFunctionPointer()
1949
+
1950
+ index = 1
1951
+ for each arg in args
1952
+ skipTest = m.doSkipTest(m.functionName)
1953
+ text = " " + index.tostr() + " of " + args.count().tostr()
1954
+ m.tests.push({ name: m.functionName + text, pointer: funcPointer, arg: arg, hasArgs: true, skip: skipTest })
1955
+ index++
1956
+ end for
1957
+ else
1958
+ ? "WARNING: Cannot find function [" providerFunction "]"
1959
+ end if
1960
+ end if
1961
+ else
1962
+ ? "WARNING: Wrong format of @ParameterizedTest \n @MethodSource(providerFunctionName)"
1963
+ ? "m.executedParametrizedAdding = "m.executedParametrizedAdding
1964
+ ? "m.annotations.methodSource = "m.annotations.methodSource
1965
+ ? "m.annotations.parameterizedTest = "m.annotations.parameterizedTest
1966
+ ? ""
1967
+ end if
1968
+ end if
1969
+ end sub
1970
+
1971
+ beforeEach: sub()
1972
+ m.beforeEachFunc = m.getFunctionPointer(m.functionName)
1973
+ end sub
1974
+
1975
+ beforeAll: sub()
1976
+ m.beforeAllFunc = m.getFunctionPointer(m.functionName)
1977
+ end sub
1978
+
1979
+ AfterEach: sub()
1980
+ m.AfterEachFunc = m.getFunctionPointer(m.functionName)
1981
+ end sub
1982
+
1983
+ AfterAll: sub()
1984
+ m.AfterAllFunc = m.getFunctionPointer(m.functionName)
1985
+ end sub
1986
+
1987
+ ignore: sub()
1988
+ funcPointer = m.getFunctionPointer(m.functionName)
1989
+ m.tests.push({ name: m.functionName, pointer: funcPointer, skip: true })
1990
+ end sub
1991
+
1992
+ doSkipTest: function(name as string)
1993
+ includeFilter = []
1994
+ excludeFilter = []
1995
+
1996
+ gthis = GetGlobalAA()
1997
+ if gthis.IncludeFilter <> invalid then includeFilter.append(gthis.IncludeFilter)
1998
+ if gthis.ExcludeFilter <> invalid then excludeFilter.append(gthis.ExcludeFilter)
1999
+
2000
+ ' apply test filters
2001
+ skipTest = false
2002
+ ' skip test if it is found in exclude filter
2003
+ for each testName in excludeFilter
2004
+ if TF_Utils__IsNotEmptyString(testName) and LCase(testName.Trim()) = LCase(name.Trim())
2005
+ skipTest = true
2006
+ exit for
2007
+ end if
2008
+ end for
2009
+
2010
+ ' skip test if it is not found in include filter
2011
+ if not skipTest and includeFilter.Count() > 0
2012
+ foundInIncludeFilter = false
2013
+
2014
+ for each testName in includeFilter
2015
+ if TF_Utils__IsNotEmptyString(testName) and LCase(testName) = LCase(name)
2016
+ foundInIncludeFilter = true
2017
+ exit for
2018
+ end if
2019
+ end for
2020
+
2021
+ skipTest = not foundInIncludeFilter
2022
+ end if
2023
+
2024
+ return skipTest
2025
+ end function
2026
+
2027
+ buildTests: sub()
2028
+ testSuite = m.testSuite
2029
+ testSuite.Name = m.filePath
2030
+ if m.beforeAllFunc <> invalid then testSuite.SetUp = m.beforeAllFunc
2031
+ if m.AfterAllFunc <> invalid then testSuite.TearDown = m.AfterAllFunc
2032
+ testSuite.IS_NEW_APPROACH = true
2033
+
2034
+ for each test in m.tests
2035
+ ' Add tests to suite's tests collection
2036
+ arg = invalid
2037
+ hasArgs = false
2038
+ if test.hasArgs <> invalid then
2039
+ arg = test.arg
2040
+ hasArgs = true
2041
+ end if
2042
+
2043
+ testSuite.addTest(test.name, test.pointer, m.beforeEachFunc, m.AfterEachFunc, arg, hasArgs, test.skip)
2044
+ end for
2045
+ end sub
2046
+
2047
+ getFunctionPointer: TestFramework__getFunctionPointer
2048
+ }
2049
+
2050
+ currentAnottations = []
2051
+ index = 0
2052
+
2053
+ for each line in souceCode.Tokenize(Chr(10))
2054
+ line = line.Trim()
2055
+ if line <> "" ' skipping empty lines
2056
+ if allowedAnnotationsRegex.IsMatch(line)
2057
+ groups = allowedAnnotationsRegex.Match(line)
2058
+ anottationType = groups[1]
2059
+ if anottationType <> invalid and processors[anottationType] <> invalid then
2060
+ currentAnottations.push(anottationType)
2061
+ processors.annotations[anottationType] = { line: line, lineIndex: index }
2062
+ end if
2063
+ else
2064
+ if currentAnottations.count() > 0 then
2065
+ isParametrized = anyArgsFunctionRegex.IsMatch(line)
2066
+ properMap = { parameterizedtest: "", methodsource: "" }
2067
+ for each availableAnottation in currentAnottations
2068
+ isParametrized = isParametrized or properMap[availableAnottation] <> invalid
2069
+ end for
2070
+
2071
+ if voidFunctionRegex.IsMatch(line) or isParametrized then
2072
+ groups = voidFunctionRegex.Match(line)
2073
+
2074
+ if isParametrized then
2075
+ groups = anyArgsFunctionRegex.Match(line)
2076
+ end if
2077
+ if groups[2] <> invalid then
2078
+ processors.functionName = groups[2]
2079
+ processors.currentLine = line
2080
+
2081
+ ' process all handlers
2082
+ if isParametrized then processors.executedParametrizedAdding = false
2083
+ for each availableAnottation in currentAnottations
2084
+ processors[availableAnottation]()
2085
+ if isParametrized then processors.executedParametrizedAdding = true
2086
+ end for
2087
+ currentAnottations = []
2088
+ processors.annotations = {}
2089
+ foundAnyTest = true
2090
+ end if
2091
+ else
2092
+ ' invalidating annotation
2093
+ ' TODO print message here that we skipped annotation
2094
+ ? "WARNING: annotation " currentAnottations " isparametrized=" isParametrized " skipped at line " index ":[" line "]"
2095
+ processors.annotations = {}
2096
+ currentAnottations = []
2097
+ end if
2098
+ end if
2099
+ end if
2100
+ end if
2101
+ index++
2102
+ end for
2103
+
2104
+ processors.buildTests()
2105
+
2106
+ if not foundAnyTest then
2107
+ testSuite = invalid
2108
+ end if
2109
+ return testSuite
2110
+ end function
2111
+
2112
+ function TestFramework__getFunctionPointer(functionName as string) as dynamic
2113
+ result = invalid
2114
+
2115
+ gthis = GetGlobalAA()
2116
+ if gthis.FunctionsList <> invalid then
2117
+ for each value in gthis.FunctionsList
2118
+ if Type(value) <> "" and LCase(Type(value)) <> "<uninitialized>" and GetInterface(value, "ifFunction") <> invalid and LCase(value.tostr()) = "function: " + LCase(functionName) then
2119
+ result = value
2120
+ exit for
2121
+ end if
2122
+ end for
2123
+ end if
2124
+
2125
+ if LCase(Type(result)) = "<uninitialized>" then result = invalid
2126
+ if result = invalid then
2127
+ if gthis.notFoundFunctionPointerList = invalid then gthis.notFoundFunctionPointerList = []
2128
+ gthis.notFoundFunctionPointerList.push(functionName)
2129
+ end if
2130
+ return result
2131
+ end function
2132
+
2133
+ sub TestRunner__SetFunctions(listOfFunctions as dynamic)
2134
+ gthis = GetGlobalAA()
2135
+
2136
+ if gthis.FunctionsList = invalid then
2137
+ gthis.FunctionsList = []
2138
+ end if
2139
+ gthis.FunctionsList.append(listOfFunctions)
2140
+ end sub
2141
+
2142
+ sub TestRunner__SetIncludeFilter(listOfFunctions as dynamic)
2143
+ gthis = GetGlobalAA()
2144
+
2145
+ if gthis.IncludeFilter = invalid
2146
+ gthis.IncludeFilter = []
2147
+ end if
2148
+
2149
+ if TF_Utils__IsArray(listOfFunctions)
2150
+ gthis.IncludeFilter.Append(listOfFunctions)
2151
+ else if TF_Utils__IsNotEmptyString(listOfFunctions)
2152
+ gthis.IncludeFilter.Append(listOfFunctions.Split(","))
2153
+ else
2154
+ ? "WARNING: Could not parse input parameters for Include Filter. Filter wont be applied."
2155
+ end if
2156
+ end sub
2157
+
2158
+ function TestRunner__GetIncludeFilter()
2159
+ gthis = GetGlobalAA()
2160
+
2161
+ if gthis.IncludeFilter = invalid
2162
+ gthis.IncludeFilter = []
2163
+ end if
2164
+
2165
+ return gthis.IncludeFilter
2166
+ end function
2167
+
2168
+ sub TestRunner__SetExcludeFilter(listOfFunctions as dynamic)
2169
+ gthis = GetGlobalAA()
2170
+
2171
+ if gthis.ExcludeFilter = invalid
2172
+ gthis.ExcludeFilter = []
2173
+ end if
2174
+
2175
+ if TF_Utils__IsArray(listOfFunctions)
2176
+ gthis.ExcludeFilter.Append(listOfFunctions)
2177
+ else if TF_Utils__IsNotEmptyString(listOfFunctions)
2178
+ gthis.ExcludeFilter.Append(listOfFunctions.Split(","))
2179
+ else
2180
+ ? "WARNING: Could not parse input parameters for Exclude Filter. Filter wont be applied."
2181
+ end if
2182
+ end sub
2183
+
2184
+ function TestRunner__GetExcludeFilter()
2185
+ gthis = GetGlobalAA()
2186
+
2187
+ if gthis.ExcludeFilter = invalid
2188
+ gthis.ExcludeFilter = []
2189
+ end if
2190
+
2191
+ return gthis.ExcludeFilter
2192
+ end function
2193
+
2194
+ ' ----------------------------------------------------------------
2195
+ ' Scans all test files for test suite function names for a given test node.
2196
+
2197
+ ' @param testNodeName (string) name of a test node, test suites for which are needed
2198
+
2199
+ ' @return An array of test suite names.
2200
+ ' ----------------------------------------------------------------
2201
+ function TestRunner__GetTestSuiteNamesList(testNodeName as string) as object
2202
+ result = []
2203
+ testSuiteRegex = CreateObject("roRegex", "^(function|sub)\s(" + m.testSuitePrefix + m.testSuiteName + "[0-9a-z\_]*)\s*\(", "i")
2204
+ testFilesList = m.GetTestFilesList(m.nodesTestDirectory, testNodeName)
2205
+
2206
+ for each filePath in testFilesList
2207
+ code = TF_Utils__AsString(ReadAsciiFile(filePath))
2208
+
2209
+ if code <> ""
2210
+ foundTestSuite = false
2211
+ for each line in code.Tokenize(Chr(10))
2212
+ line.Trim()
2213
+
2214
+ if testSuiteRegex.IsMatch(line)
2215
+ functionName = testSuiteRegex.Match(line).Peek()
2216
+ result.Push(functionName)
2217
+ foundTestSuite = true
2218
+ end if
2219
+ end for
2220
+
2221
+ if not foundTestSuite then
2222
+ ' we cannot scan for new tests as we are not in proper scope
2223
+ ' so we need to pass some data so this can be executed in render thread
2224
+ result.push({ filePath: filePath, code: code })
2225
+ end if
2226
+ end if
2227
+ end for
2228
+
2229
+ return result
2230
+ end function
2231
+
2232
+ ' ----------------------------------------------------------------
2233
+ ' Scan testsDirectory and all subdirectories for test files.
2234
+
2235
+ ' @param testsDirectory (string, optional) A target directory with test files.
2236
+ ' @param testFilePrefix (string, optional) prefix, used by test files
2237
+
2238
+ ' @return An array of test files.
2239
+ ' ----------------------------------------------------------------
2240
+ function TestRunner__GetTestFilesList(testsDirectory = m.testsDirectory as string, testFilePrefix = m.testFilePrefix as string) as object
2241
+ result = []
2242
+ testsFileRegex = CreateObject("roRegex", "^(" + testFilePrefix + ")[0-9a-z\_]*\.brs$", "i")
2243
+
2244
+ if testsDirectory <> ""
2245
+ fileSystem = CreateObject("roFileSystem")
2246
+
2247
+ if m.isNodeMode
2248
+ ? string(2, Chr(10))
2249
+ ? string(10, "!!!")
2250
+ ? "Note if you crash here this means that we are in render thread and searching for tests"
2251
+ ? "Problem is that file naming is wrong"
2252
+ ? "check brs file name they should match pattern ""Test_ExactComponentName_anything.brs"""
2253
+ ? "In this case we were looking for "testFilePrefix
2254
+ ? string(10, "!!!") string(2, Chr(10))
2255
+ end if
2256
+ listing = fileSystem.GetDirectoryListing(testsDirectory)
2257
+
2258
+ for each item in listing
2259
+ itemPath = testsDirectory + "/" + item
2260
+ itemStat = fileSystem.Stat(itemPath)
2261
+
2262
+ if itemStat.type = "directory" then
2263
+ result.Append(m.getTestFilesList(itemPath, testFilePrefix))
2264
+ else if testsFileRegex.IsMatch(item) then
2265
+ result.Push(itemPath)
2266
+ end if
2267
+ end for
2268
+ end if
2269
+
2270
+ return result
2271
+ end function
2272
+
2273
+ ' ----------------------------------------------------------------
2274
+ ' Scan nodesTestDirectory and all subdirectories for test nodes.
2275
+
2276
+ ' @param nodesTestDirectory (string, optional) A target directory with test nodes.
2277
+
2278
+ ' @return An array of test node names.
2279
+ ' ----------------------------------------------------------------
2280
+ function TestRunner__GetTestNodesList(testsDirectory = m.nodesTestDirectory as string) as object
2281
+ result = []
2282
+ testsFileRegex = CreateObject("roRegex", "^(" + m.testFilePrefix + ")[0-9a-z\_]*\.xml$", "i")
2283
+
2284
+ if testsDirectory <> ""
2285
+ fileSystem = CreateObject("roFileSystem")
2286
+ listing = fileSystem.GetDirectoryListing(testsDirectory)
2287
+
2288
+ for each item in listing
2289
+ itemPath = testsDirectory + "/" + item
2290
+ itemStat = fileSystem.Stat(itemPath)
2291
+
2292
+ if itemStat.type = "directory" then
2293
+ result.Append(m.getTestNodesList(itemPath))
2294
+ else if testsFileRegex.IsMatch(item) then
2295
+ result.Push(item.replace(".xml", ""))
2296
+ end if
2297
+ end for
2298
+ end if
2299
+
2300
+ return result
2301
+ end function
2302
+
2303
+ ' ----------------------------------------------------------------
2304
+ ' Creates and runs test runner. Should be used ONLY within a node.
2305
+
2306
+ ' @param params (array) parameters, passed from main thread, used to setup new test runner
2307
+
2308
+ ' @return statistic object.
2309
+ ' ----------------------------------------------------------------
2310
+ function TestFramework__RunNodeTests(params as object) as object
2311
+ this = params[0]
2312
+
2313
+ statObj = params[1]
2314
+ testSuiteNamesList = params[2]
2315
+
2316
+ Runner = TestRunner()
2317
+
2318
+ Runner.SetTestSuitePrefix(this.testSuitePrefix)
2319
+ Runner.SetTestFilePrefix(this.testFilePrefix)
2320
+ Runner.SetTestSuiteName(this.testSuiteName)
2321
+ Runner.SetTestCaseName(this.testCaseName)
2322
+ Runner.SetFailFast(this.failFast)
2323
+
2324
+ Runner.SetIncludeFilter(params[3])
2325
+ Runner.SetExcludeFilter(params[4])
2326
+
2327
+ return Runner.Run(statObj, testSuiteNamesList)
2328
+ end function
2329
+ function UTF_skip(msg = "")
2330
+ return UTF_PushErrorMessage(BTS__Skip(msg))
2331
+ end function
2332
+
2333
+ function UTF_fail(msg = "")
2334
+ return UTF_PushErrorMessage(BTS__Fail(msg))
2335
+ end function
2336
+
2337
+ function UTF_assertFalse(expr, msg = "Expression evaluates to true")
2338
+ return UTF_PushErrorMessage(BTS__AssertFalse(expr, msg))
2339
+ end function
2340
+
2341
+ function UTF_assertTrue(expr, msg = "Expression evaluates to false")
2342
+ return UTF_PushErrorMessage(BTS__AssertTrue(expr, msg))
2343
+ end function
2344
+
2345
+ function UTF_assertEqual(first, second, msg = "")
2346
+ return UTF_PushErrorMessage(BTS__AssertEqual(first, second, msg))
2347
+ end function
2348
+
2349
+ function UTF_assertNotEqual(first, second, msg = "")
2350
+ return UTF_PushErrorMessage(BTS__AssertNotEqual(first, second, msg))
2351
+ end function
2352
+
2353
+ function UTF_assertInvalid(value, msg = "")
2354
+ return UTF_PushErrorMessage(BTS__AssertInvalid(value, msg))
2355
+ end function
2356
+
2357
+ function UTF_assertNotInvalid(value, msg = "")
2358
+ return UTF_PushErrorMessage(BTS__AssertNotInvalid(value, msg))
2359
+ end function
2360
+
2361
+ function UTF_assertAAHasKey(array, key, msg = "")
2362
+ return UTF_PushErrorMessage(BTS__AssertAAHasKey(array, key, msg))
2363
+ end function
2364
+
2365
+ function UTF_assertAANotHasKey(array, key, msg = "")
2366
+ return UTF_PushErrorMessage(BTS__AssertAANotHasKey(array, key, msg))
2367
+ end function
2368
+
2369
+ function UTF_assertAAHasKeys(array, keys, msg = "")
2370
+ return UTF_PushErrorMessage(BTS__AssertAAHasKeys(array, keys, msg))
2371
+ end function
2372
+
2373
+ function UTF_assertAANotHasKeys(array, keys, msg = "")
2374
+ return UTF_PushErrorMessage(BTS__AssertAANotHasKeys(array, keys, msg))
2375
+ end function
2376
+
2377
+ function UTF_assertArrayContains(array, value, key = invalid, msg = "")
2378
+ return UTF_PushErrorMessage(BTS__AssertArrayContains(array, value, key, msg))
2379
+ end function
2380
+
2381
+ function UTF_assertArrayNotContains(array, value, key = invalid, msg = "")
2382
+ return UTF_PushErrorMessage(BTS__AssertArrayNotContains(array, value, key, msg))
2383
+ end function
2384
+
2385
+ function UTF_assertArrayContainsSubset(array, subset, msg = "")
2386
+ return UTF_PushErrorMessage(BTS__AssertArrayContainsSubset(array, subset, msg))
2387
+ end function
2388
+
2389
+ function UTF_assertArrayNotContainsSubset(array, subset, msg = "")
2390
+ return UTF_PushErrorMessage(BTS__AssertArrayNotContainsSubset(array, subset, msg))
2391
+ end function
2392
+
2393
+ function UTF_assertArrayCount(array, count, msg = "")
2394
+ return UTF_PushErrorMessage(BTS__AssertArrayCount(array, count, msg))
2395
+ end function
2396
+
2397
+ function UTF_assertArrayNotCount(array, count, msg = "")
2398
+ return UTF_PushErrorMessage(BTS__AssertArrayNotCount(array, count, msg))
2399
+ end function
2400
+
2401
+ function UTF_assertEmpty(item, msg = "")
2402
+ return UTF_PushErrorMessage(BTS__AssertEmpty(item, msg))
2403
+ end function
2404
+
2405
+ function UTF_assertNotEmpty(item, msg = "")
2406
+ return UTF_PushErrorMessage(BTS__AssertNotEmpty(item, msg))
2407
+ end function
2408
+
2409
+ function UTF_PushErrorMessage(message as string) as boolean
2410
+ result = Len(message) <= 0
2411
+ if not result then
2412
+ m.globalErrorsList.push(message)
2413
+ end if
2414
+
2415
+ return result
2416
+ end function'*****************************************************************
2417
+ '* Copyright Roku 2011-2019
2418
+ '* All Rights Reserved
2419
+ '*****************************************************************
2420
+ ' Common framework utility functions
2421
+ ' *****************************************************************
2422
+
2423
+ ' *************************************************
2424
+ ' TF_Utils__IsXmlElement - check if value contains XMLElement interface
2425
+ ' @param value As Dynamic
2426
+ ' @return As Boolean - true if value contains XMLElement interface, else return false
2427
+ ' *************************************************
2428
+ function TF_Utils__IsXmlElement(value as dynamic) as boolean
2429
+ return TF_Utils__IsValid(value) and GetInterface(value, "ifXMLElement") <> invalid
2430
+ end function
2431
+
2432
+ ' *************************************************
2433
+ ' TF_Utils__IsFunction - check if value contains Function interface
2434
+ ' @param value As Dynamic
2435
+ ' @return As Boolean - true if value contains Function interface, else return false
2436
+ ' *************************************************
2437
+ function TF_Utils__IsFunction(value as dynamic) as boolean
2438
+ return TF_Utils__IsValid(value) and GetInterface(value, "ifFunction") <> invalid
2439
+ end function
2440
+
2441
+ ' *************************************************
2442
+ ' TF_Utils__IsBoolean - check if value contains Boolean interface
2443
+ ' @param value As Dynamic
2444
+ ' @return As Boolean - true if value contains Boolean interface, else return false
2445
+ ' *************************************************
2446
+ function TF_Utils__IsBoolean(value as dynamic) as boolean
2447
+ return TF_Utils__IsValid(value) and GetInterface(value, "ifBoolean") <> invalid
2448
+ end function
2449
+
2450
+ ' *************************************************
2451
+ ' TF_Utils__IsInteger - check if value type equals Integer
2452
+ ' @param value As Dynamic
2453
+ ' @return As Boolean - true if value type equals Integer, else return false
2454
+ ' *************************************************
2455
+ function TF_Utils__IsInteger(value as dynamic) as boolean
2456
+ return TF_Utils__IsValid(value) and GetInterface(value, "ifInt") <> invalid and (Type(value) = "roInt" or Type(value) = "roInteger" or Type(value) = "Integer")
2457
+ end function
2458
+
2459
+ ' *************************************************
2460
+ ' TF_Utils__IsFloat - check if value contains Float interface
2461
+ ' @param value As Dynamic
2462
+ ' @return As Boolean - true if value contains Float interface, else return false
2463
+ ' *************************************************
2464
+ function TF_Utils__IsFloat(value as dynamic) as boolean
2465
+ return TF_Utils__IsValid(value) and GetInterface(value, "ifFloat") <> invalid
2466
+ end function
2467
+
2468
+ ' *************************************************
2469
+ ' TF_Utils__IsDouble - check if value contains Double interface
2470
+ ' @param value As Dynamic
2471
+ ' @return As Boolean - true if value contains Double interface, else return false
2472
+ ' *************************************************
2473
+ function TF_Utils__IsDouble(value as dynamic) as boolean
2474
+ return TF_Utils__IsValid(value) and GetInterface(value, "ifDouble") <> invalid
2475
+ end function
2476
+
2477
+ ' *************************************************
2478
+ ' TF_Utils__IsLongInteger - check if value contains LongInteger interface
2479
+ ' @param value As Dynamic
2480
+ ' @return As Boolean - true if value contains LongInteger interface, else return false
2481
+ ' *************************************************
2482
+ function TF_Utils__IsLongInteger(value as dynamic) as boolean
2483
+ return TF_Utils__IsValid(value) and GetInterface(value, "ifLongInt") <> invalid
2484
+ end function
2485
+
2486
+ ' *************************************************
2487
+ ' TF_Utils__IsNumber - check if value contains LongInteger or Integer or Double or Float interface
2488
+ ' @param value As Dynamic
2489
+ ' @return As Boolean - true if value is number, else return false
2490
+ ' *************************************************
2491
+ function TF_Utils__IsNumber(value as dynamic) as boolean
2492
+ return TF_Utils__IsLongInteger(value) or TF_Utils__IsDouble(value) or TF_Utils__IsInteger(value) or TF_Utils__IsFloat(value)
2493
+ end function
2494
+
2495
+ ' *************************************************
2496
+ ' TF_Utils__IsList - check if value contains List interface
2497
+ ' @param value As Dynamic
2498
+ ' @return As Boolean - true if value contains List interface, else return false
2499
+ ' *************************************************
2500
+ function TF_Utils__IsList(value as dynamic) as boolean
2501
+ return TF_Utils__IsValid(value) and GetInterface(value, "ifList") <> invalid
2502
+ end function
2503
+
2504
+ ' *************************************************
2505
+ ' TF_Utils__IsArray - check if value contains Array interface
2506
+ ' @param value As Dynamic
2507
+ ' @return As Boolean - true if value contains Array interface, else return false
2508
+ ' *************************************************
2509
+ function TF_Utils__IsArray(value as dynamic) as boolean
2510
+ return TF_Utils__IsValid(value) and GetInterface(value, "ifArray") <> invalid
2511
+ end function
2512
+
2513
+ ' *************************************************
2514
+ ' TF_Utils__IsAssociativeArray - check if value contains AssociativeArray interface
2515
+ ' @param value As Dynamic
2516
+ ' @return As Boolean - true if value contains AssociativeArray interface, else return false
2517
+ ' *************************************************
2518
+ function TF_Utils__IsAssociativeArray(value as dynamic) as boolean
2519
+ return TF_Utils__IsValid(value) and GetInterface(value, "ifAssociativeArray") <> invalid
2520
+ end function
2521
+
2522
+ ' *************************************************
2523
+ ' TF_Utils__IsSGNode - check if value contains SGNodeChildren interface
2524
+ ' @param value As Dynamic
2525
+ ' @return As Boolean - true if value contains SGNodeChildren interface, else return false
2526
+ ' *************************************************
2527
+ function TF_Utils__IsSGNode(value as dynamic) as boolean
2528
+ return TF_Utils__IsValid(value) and GetInterface(value, "ifSGNodeChildren") <> invalid
2529
+ end function
2530
+
2531
+ ' *************************************************
2532
+ ' TF_Utils__IsString - check if value contains String interface
2533
+ ' @param value As Dynamic
2534
+ ' @return As Boolean - true if value contains String interface, else return false
2535
+ ' *************************************************
2536
+ function TF_Utils__IsString(value as dynamic) as boolean
2537
+ return TF_Utils__IsValid(value) and GetInterface(value, "ifString") <> invalid
2538
+ end function
2539
+
2540
+ ' *************************************************
2541
+ ' TF_Utils__IsNotEmptyString - check if value contains String interface and length more 0
2542
+ ' @param value As Dynamic
2543
+ ' @return As Boolean - true if value contains String interface and length more 0, else return false
2544
+ ' *************************************************
2545
+ function TF_Utils__IsNotEmptyString(value as dynamic) as boolean
2546
+ return TF_Utils__IsString(value) and Len(value) > 0
2547
+ end function
2548
+
2549
+ ' *************************************************
2550
+ ' TF_Utils__IsDateTime - check if value contains DateTime interface
2551
+ ' @param value As Dynamic
2552
+ ' @return As Boolean - true if value contains DateTime interface, else return false
2553
+ ' *************************************************
2554
+ function TF_Utils__IsDateTime(value as dynamic) as boolean
2555
+ return TF_Utils__IsValid(value) and (GetInterface(value, "ifDateTime") <> invalid or Type(value) = "roDateTime")
2556
+ end function
2557
+
2558
+ ' *************************************************
2559
+ ' TF_Utils__IsValid - check if value initialized and not equal invalid
2560
+ ' @param value As Dynamic
2561
+ ' @return As Boolean - true if value initialized and not equal invalid, else return false
2562
+ ' *************************************************
2563
+ function TF_Utils__IsValid(value as dynamic) as boolean
2564
+ return Type(value) <> "<uninitialized>" and value <> invalid
2565
+ end function
2566
+
2567
+ ' *************************************************
2568
+ ' TF_Utils__ValidStr - return value if his contains String interface else return empty string
2569
+ ' @param value As Object
2570
+ ' @return As String - value if his contains String interface else return empty string
2571
+ ' *************************************************
2572
+ function TF_Utils__ValidStr(obj as object) as string
2573
+ if obj <> invalid and GetInterface(obj, "ifString") <> invalid
2574
+ return obj
2575
+ else
2576
+ return ""
2577
+ end if
2578
+ end function
2579
+
2580
+ ' *************************************************
2581
+ ' TF_Utils__AsString - convert input to String if this possible, else return empty string
2582
+ ' @param input As Dynamic
2583
+ ' @return As String - return converted string
2584
+ ' *************************************************
2585
+ function TF_Utils__AsString(input as dynamic) as string
2586
+ if TF_Utils__IsValid(input) = false
2587
+ return ""
2588
+ else if TF_Utils__IsString(input)
2589
+ return input
2590
+ else if TF_Utils__IsInteger(input) or TF_Utils__IsLongInteger(input) or TF_Utils__IsBoolean(input)
2591
+ return input.ToStr()
2592
+ else if TF_Utils__IsFloat(input) or TF_Utils__IsDouble(input)
2593
+ return Str(input).Trim()
2594
+ else
2595
+ return ""
2596
+ end if
2597
+ end function
2598
+
2599
+ ' *************************************************
2600
+ ' TF_Utils__AsInteger - convert input to Integer if this possible, else return 0
2601
+ ' @param input As Dynamic
2602
+ ' @return As Integer - return converted Integer
2603
+ ' *************************************************
2604
+ function TF_Utils__AsInteger(input as dynamic) as integer
2605
+ if TF_Utils__IsValid(input) = false
2606
+ return 0
2607
+ else if TF_Utils__IsString(input)
2608
+ return input.ToInt()
2609
+ else if TF_Utils__IsInteger(input)
2610
+ return input
2611
+ else if TF_Utils__IsFloat(input) or TF_Utils__IsDouble(input) or TF_Utils__IsLongInteger(input)
2612
+ return Int(input)
2613
+ else
2614
+ return 0
2615
+ end if
2616
+ end function
2617
+
2618
+ ' *************************************************
2619
+ ' TF_Utils__AsLongInteger - convert input to LongInteger if this possible, else return 0
2620
+ ' @param input As Dynamic
2621
+ ' @return As Integer - return converted LongInteger
2622
+ ' *************************************************
2623
+ function TF_Utils__AsLongInteger(input as dynamic) as longinteger
2624
+ if TF_Utils__IsValid(input) = false
2625
+ return 0
2626
+ else if TF_Utils__IsString(input)
2627
+ return TF_Utils__AsInteger(input)
2628
+ else if TF_Utils__IsLongInteger(input) or TF_Utils__IsFloat(input) or TF_Utils__IsDouble(input) or TF_Utils__IsInteger(input)
2629
+ return input
2630
+ else
2631
+ return 0
2632
+ end if
2633
+ end function
2634
+
2635
+ ' *************************************************
2636
+ ' TF_Utils__AsFloat - convert input to Float if this possible, else return 0.0
2637
+ ' @param input As Dynamic
2638
+ ' @return As Float - return converted Float
2639
+ ' *************************************************
2640
+ function TF_Utils__AsFloat(input as dynamic) as float
2641
+ if TF_Utils__IsValid(input) = false
2642
+ return 0.0
2643
+ else if TF_Utils__IsString(input)
2644
+ return input.ToFloat()
2645
+ else if TF_Utils__IsInteger(input)
2646
+ return (input / 1)
2647
+ else if TF_Utils__IsFloat(input) or TF_Utils__IsDouble(input) or TF_Utils__IsLongInteger(input)
2648
+ return input
2649
+ else
2650
+ return 0.0
2651
+ end if
2652
+ end function
2653
+
2654
+ ' *************************************************
2655
+ ' TF_Utils__AsDouble - convert input to Double if this possible, else return 0.0
2656
+ ' @param input As Dynamic
2657
+ ' @return As Float - return converted Double
2658
+ ' *************************************************
2659
+ function TF_Utils__AsDouble(input as dynamic) as double
2660
+ if TF_Utils__IsValid(input) = false
2661
+ return 0.0
2662
+ else if TF_Utils__IsString(input)
2663
+ return TF_Utils__AsFloat(input)
2664
+ else if TF_Utils__IsInteger(input) or TF_Utils__IsLongInteger(input) or TF_Utils__IsFloat(input) or TF_Utils__IsDouble(input)
2665
+ return input
2666
+ else
2667
+ return 0.0
2668
+ end if
2669
+ end function
2670
+
2671
+ ' *************************************************
2672
+ ' TF_Utils__AsBoolean - convert input to Boolean if this possible, else return False
2673
+ ' @param input As Dynamic
2674
+ ' @return As Boolean
2675
+ ' *************************************************
2676
+ function TF_Utils__AsBoolean(input as dynamic) as boolean
2677
+ if TF_Utils__IsValid(input) = false
2678
+ return false
2679
+ else if TF_Utils__IsString(input)
2680
+ return LCase(input) = "true"
2681
+ else if TF_Utils__IsInteger(input) or TF_Utils__IsFloat(input)
2682
+ return input <> 0
2683
+ else if TF_Utils__IsBoolean(input)
2684
+ return input
2685
+ else
2686
+ return false
2687
+ end if
2688
+ end function
2689
+
2690
+ ' *************************************************
2691
+ ' TF_Utils__AsArray - if type of value equals array return value, else return array with one element [value]
2692
+ ' @param value As Object
2693
+ ' @return As Object - roArray
2694
+ ' *************************************************
2695
+ function TF_Utils__AsArray(value as object) as object
2696
+ if TF_Utils__IsValid(value)
2697
+ if not TF_Utils__IsArray(value)
2698
+ return [value]
2699
+ else
2700
+ return value
2701
+ end if
2702
+ end if
2703
+ return []
2704
+ end function
2705
+
2706
+ ' =====================
2707
+ ' Strings
2708
+ ' =====================
2709
+
2710
+ ' *************************************************
2711
+ ' TF_Utils__IsNullOrEmpty - check if value is invalid or empty
2712
+ ' @param value As Dynamic
2713
+ ' @return As Boolean - true if value is null or empty string, else return false
2714
+ ' *************************************************
2715
+ function TF_Utils__IsNullOrEmpty(value as dynamic) as boolean
2716
+ if TF_Utils__IsString(value)
2717
+ return Len(value) = 0
2718
+ else
2719
+ return not TF_Utils__IsValid(value)
2720
+ end if
2721
+ end function
2722
+
2723
+ ' =====================
2724
+ ' Arrays
2725
+ ' =====================
2726
+
2727
+ ' *************************************************
2728
+ ' TF_Utils__FindElementIndexInArray - find an element index in array
2729
+ ' @param array As Object
2730
+ ' @param value As Object
2731
+ ' @param compareAttribute As Dynamic
2732
+ ' @param caseSensitive As Boolean
2733
+ ' @return As Integer - element index if array contains a value, else return -1
2734
+ ' *************************************************
2735
+ function TF_Utils__FindElementIndexInArray(array as object, value as object, compareAttribute = invalid as dynamic, caseSensitive = false as boolean) as integer
2736
+ if TF_Utils__IsArray(array)
2737
+ for i = 0 to TF_Utils__AsArray(array).Count() - 1
2738
+ compareValue = array[i]
2739
+
2740
+ if compareAttribute <> invalid and TF_Utils__IsAssociativeArray(compareValue) and compareValue.DoesExist(compareAttribute)
2741
+ compareValue = compareValue.LookupCI(compareAttribute)
2742
+ end if
2743
+
2744
+ if TF_Utils__IsString(compareValue) and TF_Utils__IsString(value) and not caseSensitive
2745
+ if LCase(compareValue) = LCase(value)
2746
+ return i
2747
+ end if
2748
+ else if TF_Utils__BaseComparator(compareValue, value)
2749
+ return i
2750
+ end if
2751
+
2752
+ item = array[i]
2753
+ next
2754
+ end if
2755
+
2756
+ return -1
2757
+ end function
2758
+
2759
+ ' *************************************************
2760
+ ' TF_Utils__ArrayContains - check if array contains specified value
2761
+ ' @param array As Object
2762
+ ' @param value As Object
2763
+ ' @param compareAttribute As Dynamic
2764
+ ' @return As Boolean - true if array contains a value, else return false
2765
+ ' *************************************************
2766
+ function TF_Utils__ArrayContains(array as object, value as object, compareAttribute = invalid as dynamic) as boolean
2767
+ return (TF_Utils__FindElementIndexInArray(array, value, compareAttribute) > -1)
2768
+ end function
2769
+
2770
+ ' ----------------------------------------------------------------
2771
+ ' Type Comparison Functionality
2772
+ ' ----------------------------------------------------------------
2773
+
2774
+ ' ----------------------------------------------------------------
2775
+ ' Compare two arbitrary values to each other.
2776
+
2777
+ ' @param Value1 (dynamic) A first item to compare.
2778
+ ' @param Value2 (dynamic) A second item to compare.
2779
+ ' @param comparator (Function, optional) Function, to compare 2 values. Should take in 2 parameters and return either true or false.
2780
+
2781
+ ' @return True if values are equal or False in other case.
2782
+ ' ----------------------------------------------------------------
2783
+ function TF_Utils__EqValues(Value1 as dynamic, Value2 as dynamic, comparator = invalid as object) as boolean
2784
+ if comparator = invalid
2785
+ return TF_Utils__BaseComparator(value1, value2)
2786
+ else
2787
+ return comparator(value1, value2)
2788
+ end if
2789
+ end function
2790
+
2791
+ ' ----------------------------------------------------------------
2792
+ ' Base comparator for comparing two values.
2793
+
2794
+ ' @param Value1 (dynamic) A first item to compare.
2795
+ ' @param Value2 (dynamic) A second item to compare.
2796
+
2797
+ ' @return True if values are equal or False in other case.
2798
+ function TF_Utils__BaseComparator(value1 as dynamic, value2 as dynamic) as boolean
2799
+ value1Type = Type(value1)
2800
+ value2Type = Type(value2)
2801
+
2802
+ if (value1Type = "roList" or value1Type = "roArray") and (value2Type = "roList" or value2Type = "roArray")
2803
+ return TF_Utils__EqArray(value1, value2)
2804
+ else if value1Type = "roAssociativeArray" and value2Type = "roAssociativeArray"
2805
+ return TF_Utils__EqAssocArray(value1, value2)
2806
+ else if Type(box(value1), 3) = Type(box(value2), 3)
2807
+ return value1 = value2
2808
+ else
2809
+ return false
2810
+ end if
2811
+ end function
2812
+
2813
+ ' ----------------------------------------------------------------
2814
+ ' Compare two roAssociativeArray objects for equality.
2815
+
2816
+ ' @param Value1 (object) A first associative array.
2817
+ ' @param Value2 (object) A second associative array.
2818
+
2819
+ ' @return True if arrays are equal or False in other case.
2820
+ ' ----------------------------------------------------------------
2821
+ function TF_Utils__EqAssocArray(Value1 as object, Value2 as object) as boolean
2822
+ l1 = Value1.Count()
2823
+ l2 = Value2.Count()
2824
+
2825
+ if not l1 = l2
2826
+ return false
2827
+ else
2828
+ for each k in Value1
2829
+ if not Value2.DoesExist(k)
2830
+ return false
2831
+ else
2832
+ v1 = Value1[k]
2833
+ v2 = Value2[k]
2834
+ if not TF_Utils__EqValues(v1, v2)
2835
+ return false
2836
+ end if
2837
+ end if
2838
+ end for
2839
+ return true
2840
+ end if
2841
+ end function
2842
+
2843
+ ' ----------------------------------------------------------------
2844
+ ' Compare two roArray objects for equality.
2845
+
2846
+ ' @param Value1 (object) A first array.
2847
+ ' @param Value2 (object) A second array.
2848
+
2849
+ ' @return True if arrays are equal or False in other case.
2850
+ ' ----------------------------------------------------------------
2851
+ function TF_Utils__EqArray(Value1 as object, Value2 as object) as boolean
2852
+ l1 = Value1.Count()
2853
+ l2 = Value2.Count()
2854
+
2855
+ if not l1 = l2
2856
+ return false
2857
+ else
2858
+ for i = 0 to l1 - 1
2859
+ v1 = Value1[i]
2860
+ v2 = Value2[i]
2861
+ if not TF_Utils__EqValues(v1, v2) then
2862
+ return false
2863
+ end if
2864
+ end for
2865
+ return true
2866
+ end if
2867
+ end function