mparticle-roku-sdk 2.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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