arduino_ci 1.1.0 → 1.2.0

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.
@@ -69,6 +69,23 @@ class Test
69
69
  }
70
70
  }
71
71
 
72
+ // non-comparative assert
73
+ void onAssert(
74
+ const char* file,
75
+ int line,
76
+ const char* description,
77
+ bool pass
78
+ ) {
79
+ cerr << " " << (pass ? "" : "not ") << "ok " << ++mAssertCounter << " - " << description << endl;
80
+ if (!pass) {
81
+ cerr << " ---" << endl;
82
+ cerr << " at:" << endl;
83
+ cerr << " file: " << file << endl;
84
+ cerr << " line: " << line << endl;
85
+ cerr << " ..." << endl;
86
+ }
87
+ }
88
+
72
89
  template <typename A, typename B> void onAssert(
73
90
  const char* file,
74
91
  int line,
@@ -194,6 +211,21 @@ class Test
194
211
  excise();
195
212
  }
196
213
 
214
+ bool assertion(
215
+ const char *file,
216
+ int line,
217
+ const char *description,
218
+ bool ok)
219
+ {
220
+ if (mReporter) {
221
+ mReporter->onAssert(file, line, description, ok);
222
+ }
223
+
224
+ if (!ok)
225
+ fail();
226
+ return ok;
227
+ }
228
+
197
229
  template <typename A, typename B>
198
230
  bool assertion(
199
231
  const char *file,
@@ -6,9 +6,19 @@
6
6
 
7
7
  #include "Compare.h"
8
8
 
9
- #define testBehaviorOp(die, desc, rel1, arg1, op, op_name, rel2, arg2) \
9
+ #define arduinoCITestBehaviorExp(die, desc, pass) \
10
+ do \
11
+ { \
12
+ if (!assertion(__FILE__, __LINE__, \
13
+ desc, pass)) \
14
+ { \
15
+ if (die) return; \
16
+ } \
17
+ } while (0)
18
+
19
+ #define arduinoCITestBehaviorOp(die, desc, rel1, arg1, op, op_name, rel2, arg2) \
10
20
  do \
11
- { \
21
+ { \
12
22
  if (!assertion<typeof(arg1), typeof(arg2)>(__FILE__, __LINE__, \
13
23
  desc, \
14
24
  rel1, #arg1, (arg1), \
@@ -22,34 +32,52 @@
22
32
 
23
33
 
24
34
  // helper define for the operators below
25
- #define assertOp(desc, rel1, arg1, op, op_name, rel2, arg2) \
26
- testBehaviorOp(false, desc, rel1, arg1, op, op_name, rel2, arg2)
35
+ #define arduinoCIAssertOp(desc, rel1, arg1, op, op_name, rel2, arg2) \
36
+ arduinoCITestBehaviorOp(false, "assert" desc, rel1, arg1, op, op_name, rel2, arg2)
27
37
 
28
- #define assureOp(desc, rel1, arg1, op, op_name, rel2, arg2) \
29
- testBehaviorOp(true, desc, rel1, arg1, op, op_name, rel2, arg2)
38
+ #define arduinoCIAssureOp(desc, rel1, arg1, op, op_name, rel2, arg2) \
39
+ arduinoCITestBehaviorOp(true, "assure" desc, rel1, arg1, op, op_name, rel2, arg2)
30
40
 
31
41
 
32
42
  /** macro generates optional output and calls fail() but does not return if false. */
33
- #define assertEqual(arg1,arg2) assertOp("assertEqual","expected",arg1,compareEqual,"==","actual",arg2)
34
- #define assertNotEqual(arg1,arg2) assertOp("assertNotEqual","unwanted",arg1,compareNotEqual,"!=","actual",arg2)
35
- #define assertLess(arg1,arg2) assertOp("assertLess","lowerBound",arg1,compareLess,"<","upperBound",arg2)
36
- #define assertMore(arg1,arg2) assertOp("assertMore","upperBound",arg1,compareMore,">","lowerBound",arg2)
37
- #define assertLessOrEqual(arg1,arg2) assertOp("assertLessOrEqual","lowerBound",arg1,compareLessOrEqual,"<=","upperBound",arg2)
38
- #define assertMoreOrEqual(arg1,arg2) assertOp("assertMoreOrEqual","upperBound",arg1,compareMoreOrEqual,">=","lowerBound",arg2)
39
- #define assertTrue(arg) assertEqual(true, arg)
40
- #define assertFalse(arg) assertEqual(false, arg)
41
- #define assertNull(arg) assertEqual((void*)NULL, (void*)arg)
42
- #define assertNotNull(arg) assertNotEqual((void*)NULL, (void*)arg)
43
+ #define assertTrue(arg) arduinoCITestBehaviorExp(false, "True " #arg, (arg))
44
+ #define assertFalse(arg) arduinoCITestBehaviorExp(false, "False " #arg, !(arg))
45
+ #define assertNull(arg) arduinoCITestBehaviorExp(false, "Null " #arg, ((void*)NULL == (void*)(arg)))
46
+ #define assertNotNull(arg) arduinoCITestBehaviorExp(false, "NotNull " #arg, ((void*)NULL != (void*)(arg)))
47
+ #define assertEqual(arg1,arg2) arduinoCIAssertOp("Equal","expected",arg1,compareEqual,"==","actual",arg2)
48
+ #define assertNotEqual(arg1,arg2) arduinoCIAssertOp("NotEqual","unwanted",arg1,compareNotEqual,"!=","actual",arg2)
49
+ #define assertComparativeEquivalent(arg1,arg2) arduinoCIAssertOp("ComparativeEquivalent","expected",arg1,compareEquivalent,"!<>","actual",arg2)
50
+ #define assertComparativeNotEquivalent(arg1,arg2) arduinoCIAssertOp("ComparativeNotEquivalent","unwanted",arg1,compareNotEquivalent,"<>","actual",arg2)
51
+ #define assertLess(arg1,arg2) arduinoCIAssertOp("Less","lowerBound",arg1,compareLess,"<","actual",arg2)
52
+ #define assertMore(arg1,arg2) arduinoCIAssertOp("More","upperBound",arg1,compareMore,">","actual",arg2)
53
+ #define assertLessOrEqual(arg1,arg2) arduinoCIAssertOp("LessOrEqual","lowerBound",arg1,compareLessOrEqual,"<=","actual",arg2)
54
+ #define assertMoreOrEqual(arg1,arg2) arduinoCIAssertOp("MoreOrEqual","upperBound",arg1,compareMoreOrEqual,">=","actual",arg2)
55
+
56
+ #define assertEqualFloat(arg1, arg2, arg3) arduinoCIAssertOp("EqualFloat", "epsilon", arg3, compareMoreOrEqual, ">=", "actualDifference", fabs(arg1 - arg2))
57
+ #define assertNotEqualFloat(arg1, arg2, arg3) arduinoCIAssertOp("NotEqualFloat", "epsilon", arg3, compareLessOrEqual, "<=", "insufficientDifference", fabs(arg1 - arg2))
58
+ #define assertInfinity(arg) arduinoCITestBehaviorExp(false, "Infinity " #arg, isinf(arg))
59
+ #define assertNotInfinity(arg) arduinoCITestBehaviorExp(false, "NotInfinity " #arg, !isinf(arg))
60
+ #define assertNAN(arg) arduinoCITestBehaviorExp(false, "NAN " #arg, isnan(arg))
61
+ #define assertNotNAN(arg) arduinoCITestBehaviorExp(false, "NotNAN " #arg, !isnan(arg))
62
+
43
63
 
44
64
  /** macro generates optional output and calls fail() followed by a return if false. */
45
- #define assureEqual(arg1,arg2) assureOp("assureEqual","expected",arg1,compareEqual,"==","actual",arg2)
46
- #define assureNotEqual(arg1,arg2) assureOp("assureNotEqual","unwanted",arg1,compareNotEqual,"!=","actual",arg2)
47
- #define assureLess(arg1,arg2) assureOp("assureLess","lowerBound",arg1,compareLess,"<","upperBound",arg2)
48
- #define assureMore(arg1,arg2) assureOp("assureMore","upperBound",arg1,compareMore,">","lowerBound",arg2)
49
- #define assureLessOrEqual(arg1,arg2) assureOp("assureLessOrEqual","lowerBound",arg1,compareLessOrEqual,"<=","upperBound",arg2)
50
- #define assureMoreOrEqual(arg1,arg2) assureOp("assureMoreOrEqual","upperBound",arg1,compareMoreOrEqual,">=","lowerBound",arg2)
51
- #define assureTrue(arg) assureEqual(true, arg)
52
- #define assureFalse(arg) assureEqual(false, arg)
53
- #define assureNull(arg) assureEqual((void*)NULL, (void*)arg)
54
- #define assureNotNull(arg) assureNotEqual((void*)NULL, (void*)arg)
65
+ #define assureTrue(arg) arduinoCITestBehaviorExp(true, "True " #arg, (arg))
66
+ #define assureFalse(arg) arduinoCITestBehaviorExp(true, "False " #arg, !(arg))
67
+ #define assureNull(arg) arduinoCITestBehaviorExp(true, "Null " #arg, ((void*)NULL == (void*)(arg)))
68
+ #define assureNotNull(arg) arduinoCITestBehaviorExp(true, "NotNull " #arg, ((void*)NULL != (void*)(arg)))
69
+ #define assureEqual(arg1,arg2) arduinoCIAssureOp("Equal","expected",arg1,compareEqual,"==","actual",arg2)
70
+ #define assureNotEqual(arg1,arg2) arduinoCIAssureOp("NotEqual","unwanted",arg1,compareNotEqual,"!=","actual",arg2)
71
+ #define assureComparativeEquivalent(arg1,arg2) arduinoCIAssureOp("ComparativeEquivalent","expected",arg1,compareEquivalent,"!<>","actual",arg2)
72
+ #define assureComparativeNotEquivalent(arg1,arg2) arduinoCIAssureOp("ComparativeNotEquivalent","unwanted",arg1,compareNotEquivalent,"<>","actual",arg2)
73
+ #define assureLess(arg1,arg2) arduinoCIAssureOp("Less","lowerBound",arg1,compareLess,"<","actual",arg2)
74
+ #define assureMore(arg1,arg2) arduinoCIAssureOp("More","upperBound",arg1,compareMore,">","actual",arg2)
75
+ #define assureLessOrEqual(arg1,arg2) arduinoCIAssureOp("LessOrEqual","lowerBound",arg1,compareLessOrEqual,"<=","actual",arg2)
76
+ #define assureMoreOrEqual(arg1,arg2) arduinoCIAssureOp("MoreOrEqual","upperBound",arg1,compareMoreOrEqual,">=","actual",arg2)
55
77
 
78
+ #define assureEqualFloat(arg1, arg2, arg3) arduinoCIAssureOp("EqualFloat", "epsilon", arg3, compareMoreOrEqual, ">=", "actualDifference", fabs(arg1 - arg2))
79
+ #define assureNotEqualFloat(arg1, arg2, arg3) arduinoCIAssureOp("NotEqualFloat", "epsilon", arg3, compareLessOrEqual, "<=", "insufficientDifference", fabs(arg1 - arg2))
80
+ #define assureInfinity(arg) arduinoCITestBehaviorExp(true, "Infinity " #arg, isinf(arg))
81
+ #define assureNotInfinity(arg) arduinoCITestBehaviorExp(true, "NotInfinity " #arg, !isinf(arg))
82
+ #define assureNAN(arg) arduinoCITestBehaviorExp(true, "NAN " #arg, isnan(arg))
83
+ #define assureNotNAN(arg) arduinoCITestBehaviorExp(true, "NotNAN " #arg, !isnan(arg))
@@ -10,12 +10,14 @@ template < typename A, typename B > struct Compare
10
10
  if (b<a) return 1;
11
11
  return 0;
12
12
  }
13
- inline static bool equal(const A &a,const B &b) { return (!(a < b)) && (!(b < a)); }
14
- inline static bool notEqual(const A &a,const B &b) { return (a<b) || (b<a); }
15
- inline static bool less(const A &a,const B &b) { return a<b; }
16
- inline static bool more(const A &a,const B &b) { return b<a; }
17
- inline static bool lessOrEqual(const A &a,const B &b) { return !(b<a); }
18
- inline static bool moreOrEqual(const A &a,const B &b) { return !(a<b); }
13
+ inline static bool equal(const A &a,const B &b) { return a==b; }
14
+ inline static bool notEqual(const A &a,const B &b) { return a!=b; }
15
+ inline static bool equivalent(const A &a,const B &b) { return (!(a < b)) && (!(b < a)); }
16
+ inline static bool notEquivalent(const A &a,const B &b) { return (a<b) || (b<a); }
17
+ inline static bool less(const A &a,const B &b) { return a<b; }
18
+ inline static bool more(const A &a,const B &b) { return b<a; }
19
+ inline static bool lessOrEqual(const A &a,const B &b) { return !(b<a); }
20
+ inline static bool moreOrEqual(const A &a,const B &b) { return !(a<b); }
19
21
  };
20
22
 
21
23
  // helpers for macros
@@ -57,56 +59,61 @@ inline static int arduinoCICompareBetween(const __FlashStringHelper * const &a,c
57
59
 
58
60
 
59
61
  // this macro works for all the string-based comparisons
62
+ // note that it substitutes equivalence for equality
60
63
  // but just in case, https://stackoverflow.com/a/13842784/2063546
61
- #define comparisonTemplateMacro(T1, T1m, T2, T2m, betweenImpl, ...) \
62
- template < __VA_ARGS__ > struct Compare<T1 T1m, T2 T2m>; \
63
- template < __VA_ARGS__ > struct Compare<T1 T1m, T2 T2m> \
64
- { \
65
- inline static int between( T1 const (&a)T1m, T2 const (&b)T2m) { return betweenImpl; } \
66
- inline static bool equal( T1 const (&a)T1m, T2 const (&b)T2m) { return between(a, b) == 0; } \
67
- inline static bool notEqual( T1 const (&a)T1m, T2 const (&b)T2m) { return between(a, b) != 0; } \
68
- inline static bool less( T1 const (&a)T1m, T2 const (&b)T2m) { return between(a, b) < 0; } \
69
- inline static bool more( T1 const (&a)T1m, T2 const (&b)T2m) { return between(a, b) > 0; } \
70
- inline static bool lessOrEqual(T1 const (&a)T1m, T2 const (&b)T2m) { return between(a, b) <= 0; } \
71
- inline static bool moreOrEqual(T1 const (&a)T1m, T2 const (&b)T2m) { return between(a, b) >= 0; } \
64
+ #define eqComparisonTemplateMacro(T1, T1m, T2, T2m, betweenImpl, ...) \
65
+ template < __VA_ARGS__ > struct Compare<T1 T1m, T2 T2m>; \
66
+ template < __VA_ARGS__ > struct Compare<T1 T1m, T2 T2m> \
67
+ { \
68
+ inline static int between( T1 const (&a)T1m, T2 const (&b)T2m) { return betweenImpl; } \
69
+ inline static bool equal( T1 const (&a)T1m, T2 const (&b)T2m) { return between(a, b) == 0; } \
70
+ inline static bool notEqual( T1 const (&a)T1m, T2 const (&b)T2m) { return between(a, b) != 0; } \
71
+ inline static bool equivalent( T1 const (&a)T1m, T2 const (&b)T2m) { return between(a, b) == 0; } \
72
+ inline static bool notEquivalent(T1 const (&a)T1m, T2 const (&b)T2m) { return between(a, b) != 0; } \
73
+ inline static bool less( T1 const (&a)T1m, T2 const (&b)T2m) { return between(a, b) < 0; } \
74
+ inline static bool more( T1 const (&a)T1m, T2 const (&b)T2m) { return between(a, b) > 0; } \
75
+ inline static bool lessOrEqual( T1 const (&a)T1m, T2 const (&b)T2m) { return between(a, b) <= 0; } \
76
+ inline static bool moreOrEqual( T1 const (&a)T1m, T2 const (&b)T2m) { return between(a, b) >= 0; } \
72
77
  };
73
78
 
74
- comparisonTemplateMacro(String, , String, , a.compareTo(b))
75
- comparisonTemplateMacro(String, , const char *, , a.compareTo(b))
79
+ eqComparisonTemplateMacro(String, , String, , a.compareTo(b))
80
+ eqComparisonTemplateMacro(String, , const char *, , a.compareTo(b))
76
81
  #if defined(F)
77
- comparisonTemplateMacro(String, , const __FlashStringHelper *, , arduinoCICompareBetween(a, b))
78
- comparisonTemplateMacro(const char *,, const __FlashStringHelper *, , strcmp_P(a,(const char *)b))
79
- comparisonTemplateMacro(const __FlashStringHelper *, , String, , -arduinoCICompareBetween(b, a))
80
- comparisonTemplateMacro(const __FlashStringHelper *, , const char *, , -strcmp_P(b,(const char *)a))
81
- comparisonTemplateMacro(const __FlashStringHelper *, , const __FlashStringHelper *, , arduinoCICompareBetween(a, b))
82
- comparisonTemplateMacro(const __FlashStringHelper *, , char *, , -strcmp_P(b,(const char *)a))
83
- comparisonTemplateMacro(char *, , const __FlashStringHelper *, , strcmp_P(a,(const char *)b))
84
- comparisonTemplateMacro(const __FlashStringHelper *, , char, [M], -strcmp_P(b,(const char *)a), size_t M)
85
- comparisonTemplateMacro(char, [N], const __FlashStringHelper *, , strcmp_P(a,(const char *)b), size_t N)
82
+ eqComparisonTemplateMacro(String, , const __FlashStringHelper *, , arduinoCICompareBetween(a, b))
83
+ eqComparisonTemplateMacro(const char *,, const __FlashStringHelper *, , strcmp_P(a,(const char *)b))
84
+ eqComparisonTemplateMacro(const __FlashStringHelper *, , String, , -arduinoCICompareBetween(b, a))
85
+ eqComparisonTemplateMacro(const __FlashStringHelper *, , const char *, , -strcmp_P(b,(const char *)a))
86
+ eqComparisonTemplateMacro(const __FlashStringHelper *, , const __FlashStringHelper *, , arduinoCICompareBetween(a, b))
87
+ eqComparisonTemplateMacro(const __FlashStringHelper *, , char *, , -strcmp_P(b,(const char *)a))
88
+ eqComparisonTemplateMacro(char *, , const __FlashStringHelper *, , strcmp_P(a,(const char *)b))
89
+ eqComparisonTemplateMacro(const __FlashStringHelper *, , char, [M], -strcmp_P(b,(const char *)a), size_t M)
90
+ eqComparisonTemplateMacro(char, [N], const __FlashStringHelper *, , strcmp_P(a,(const char *)b), size_t N)
86
91
  #endif
87
- comparisonTemplateMacro(String, , char *, , a.compareTo(b))
88
- comparisonTemplateMacro(const char *, , String, , -b.compareTo(a))
89
- comparisonTemplateMacro(const char *, , const char *, , strcmp(a,b))
90
- comparisonTemplateMacro(const char *, , char *, , strcmp(a,b))
91
- comparisonTemplateMacro(char *, , String, , -b.compareTo(a))
92
- comparisonTemplateMacro(char *, , const char *, , strcmp(a,b))
93
- comparisonTemplateMacro(char *, , char *, , strcmp(a,b))
94
- comparisonTemplateMacro(String, , char, [M], a.compareTo(b), size_t M)
95
- comparisonTemplateMacro(const char *, , char, [M], strcmp(a,b), size_t M)
96
- comparisonTemplateMacro(char *, , char, [M], strcmp(a,b), size_t M)
97
- comparisonTemplateMacro(char, [N], String, , -b.compareTo(a), size_t N)
98
- comparisonTemplateMacro(char, [N], const char *, , strcmp(a,b), size_t N)
99
- comparisonTemplateMacro(char, [N], char *, , strcmp(a,b), size_t N)
100
- comparisonTemplateMacro(char, [N], char, [M], strcmp(a,b), size_t N, size_t M)
92
+ eqComparisonTemplateMacro(String, , char *, , a.compareTo(b))
93
+ eqComparisonTemplateMacro(const char *, , String, , -b.compareTo(a))
94
+ eqComparisonTemplateMacro(const char *, , const char *, , strcmp(a,b))
95
+ eqComparisonTemplateMacro(const char *, , char *, , strcmp(a,b))
96
+ eqComparisonTemplateMacro(char *, , String, , -b.compareTo(a))
97
+ eqComparisonTemplateMacro(char *, , const char *, , strcmp(a,b))
98
+ eqComparisonTemplateMacro(char *, , char *, , strcmp(a,b))
99
+ eqComparisonTemplateMacro(String, , char, [M], a.compareTo(b), size_t M)
100
+ eqComparisonTemplateMacro(const char *, , char, [M], strcmp(a,b), size_t M)
101
+ eqComparisonTemplateMacro(char *, , char, [M], strcmp(a,b), size_t M)
102
+ eqComparisonTemplateMacro(char, [N], String, , -b.compareTo(a), size_t N)
103
+ eqComparisonTemplateMacro(char, [N], const char *, , strcmp(a,b), size_t N)
104
+ eqComparisonTemplateMacro(char, [N], char *, , strcmp(a,b), size_t N)
105
+ eqComparisonTemplateMacro(char, [N], char, [M], strcmp(a,b), size_t N, size_t M)
101
106
 
102
- comparisonTemplateMacro(A, , std::nullptr_t, , a ? 1 : 0, typename A)
103
- comparisonTemplateMacro(std::nullptr_t, , B, , b ? -1 : 0, typename B)
107
+ eqComparisonTemplateMacro(A, , std::nullptr_t, , a ? 1 : 0, typename A)
108
+ eqComparisonTemplateMacro(std::nullptr_t, , B, , b ? -1 : 0, typename B)
104
109
 
105
110
  // super general comparisons
106
- template <typename A, typename B> int compareBetween( const A &a, const B &b) { return Compare<A, B>::between( a, b); }
107
- template <typename A, typename B> bool compareEqual( const A &a, const B &b) { return Compare<A, B>::equal( a, b); }
108
- template <typename A, typename B> bool compareNotEqual( const A &a, const B &b) { return Compare<A, B>::notEqual( a, b); }
109
- template <typename A, typename B> bool compareLess( const A &a, const B &b) { return Compare<A, B>::less( a, b); }
110
- template <typename A, typename B> bool compareMore( const A &a, const B &b) { return Compare<A, B>::more( a, b); }
111
- template <typename A, typename B> bool compareLessOrEqual(const A &a, const B &b) { return Compare<A, B>::lessOrEqual(a, b); }
112
- template <typename A, typename B> bool compareMoreOrEqual(const A &a, const B &b) { return Compare<A, B>::moreOrEqual(a, b); }
111
+ template <typename A, typename B> int compareBetween( const A &a, const B &b) { return Compare<A, B>::between( a, b); }
112
+ template <typename A, typename B> bool compareEqual( const A &a, const B &b) { return Compare<A, B>::equal( a, b); }
113
+ template <typename A, typename B> bool compareNotEqual( const A &a, const B &b) { return Compare<A, B>::notEqual( a, b); }
114
+ template <typename A, typename B> bool compareEquivalent( const A &a, const B &b) { return Compare<A, B>::equivalent( a, b); }
115
+ template <typename A, typename B> bool compareNotEquivalent(const A &a, const B &b) { return Compare<A, B>::notEquivalent(a, b); }
116
+ template <typename A, typename B> bool compareLess( const A &a, const B &b) { return Compare<A, B>::less( a, b); }
117
+ template <typename A, typename B> bool compareMore( const A &a, const B &b) { return Compare<A, B>::more( a, b); }
118
+ template <typename A, typename B> bool compareLessOrEqual( const A &a, const B &b) { return Compare<A, B>::lessOrEqual( a, b); }
119
+ template <typename A, typename B> bool compareMoreOrEqual( const A &a, const B &b) { return Compare<A, B>::moreOrEqual( a, b); }
@@ -5,8 +5,11 @@ require 'pathname'
5
5
  require 'optparse'
6
6
 
7
7
  WIDTH = 80
8
- VAR_EXPECT_EXAMPLES = "EXPECT_EXAMPLES".freeze
9
- VAR_EXPECT_UNITTESTS = "EXPECT_UNITTESTS".freeze
8
+ VAR_CUSTOM_INIT_SCRIPT = "CUSTOM_INIT_SCRIPT".freeze
9
+ VAR_USE_SUBDIR = "USE_SUBDIR".freeze
10
+ VAR_EXPECT_EXAMPLES = "EXPECT_EXAMPLES".freeze
11
+ VAR_EXPECT_UNITTESTS = "EXPECT_UNITTESTS".freeze
12
+ VAR_SKIP_LIBPROPS = "SKIP_LIBRARY_PROPERTIES".freeze
10
13
 
11
14
  @failure_count = 0
12
15
  @passfail = proc { |result| result ? "✓" : "✗" }
@@ -19,6 +22,7 @@ class Parser
19
22
  output_options = {
20
23
  skip_unittests: false,
21
24
  skip_compilation: false,
25
+ skip_library_properties: false,
22
26
  ci_config: {
23
27
  "unittest" => unit_config
24
28
  },
@@ -35,6 +39,10 @@ class Parser
35
39
  output_options[:skip_compilation] = p
36
40
  end
37
41
 
42
+ opts.on("--skip-library-properties", "Don't validate library.properties entries") do |p|
43
+ output_options[:skip_compilation] = p
44
+ end
45
+
38
46
  opts.on("--testfile-select=GLOB", "Unit test file (or glob) to select") do |p|
39
47
  unit_config["testfiles"] ||= {}
40
48
  unit_config["testfiles"]["select"] ||= []
@@ -51,8 +59,12 @@ class Parser
51
59
  puts opts
52
60
  puts
53
61
  puts "Additionally, the following environment variables control the script:"
62
+ puts " - #{VAR_CUSTOM_INIT_SCRIPT} - if set, this script will be run from the Arduino/libraries directory"
63
+ puts " prior to any automated library installation or testing (e.g. to install unoffical libraries)"
64
+ puts " - #{VAR_USE_SUBDIR} - if set, the script will install the library from this subdirectory of the cwd"
54
65
  puts " - #{VAR_EXPECT_EXAMPLES} - if set, testing will fail if no example sketches are present"
55
66
  puts " - #{VAR_EXPECT_UNITTESTS} - if set, testing will fail if no unit tests are present"
67
+ puts " - #{VAR_SKIP_LIBPROPS} - if set, testing will skip [experimental] library.properties validation"
56
68
  exit
57
69
  end
58
70
  end
@@ -68,11 +80,12 @@ end
68
80
  # terminate after printing any debug info. TODO: capture debug info
69
81
  def terminate(final = nil)
70
82
  puts "Failures: #{@failure_count}"
71
- unless @failure_count.zero? || final
72
- puts "Last message: #{@backend.last_msg}"
73
- puts "========== Stdout:"
83
+ unless @failure_count.zero? || final || @backend.nil?
84
+ puts "========== Last backend command (if relevant):"
85
+ puts @backend.last_msg.to_s
86
+ puts "========== Backend Stdout:"
74
87
  puts @backend.last_out
75
- puts "========== Stderr:"
88
+ puts "========== Backend Stderr:"
76
89
  puts @backend.last_err
77
90
  end
78
91
  retcode = @failure_count.zero? ? 0 : 1
@@ -141,6 +154,10 @@ def inform_multiline(message, &block)
141
154
  perform_action(message, true, nil, nil, false, false, &block)
142
155
  end
143
156
 
157
+ def warn(message)
158
+ inform("WARNING") { message }
159
+ end
160
+
144
161
  # Assure that a platform exists and return its definition
145
162
  def assured_platform(purpose, name, config)
146
163
  platform_definition = config.platform_definition(name)
@@ -190,28 +207,20 @@ def install_arduino_library_dependencies(library_names, on_behalf_of, already_in
190
207
  installed
191
208
  end
192
209
 
193
- # @param example_platform_info [Hash] mapping of platform name to package information
194
- # @param board_package_url [Hash] mapping of package name to URL
195
- def install_all_packages(example_platform_info, board_package_url)
196
- # with all platform info, we can extract unique packages and their urls
197
- # do that, set the URLs, and download the packages
198
- all_packages = example_platform_info.values.map { |v| v[:package] }.uniq.reject(&:nil?)
199
-
200
- # make sure any non-builtin package has a URL defined
201
- all_packages.each { |p| assure("Board package #{p} has a defined URL") { board_package_url[p] } }
202
-
203
- # set up all the board manager URLs.
204
- # we can safely reject nils now, they would be for the builtins
205
- all_urls = all_packages.map { |p| board_package_url[p] }.uniq.reject(&:nil?)
206
- unless all_urls.empty?
207
- assure_multiline("Setting board manager URLs") do
208
- @backend.board_manager_urls = all_urls
209
- result = @backend.board_manager_urls
210
- result.each { |u| puts " #{u}" }
211
- (all_urls - result).empty? # check that all_urls is completely contained in the result
212
- end
210
+ # @param platforms [Array<String>] list of platforms to consider
211
+ # @param specific_config [CIConfig] configuration to use
212
+ def install_all_packages(platforms, specific_config)
213
+
214
+ # get packages from platforms
215
+ all_packages = specific_config.platform_info.select { |p, _| platforms.include?(p) }.values.map { |v| v[:package] }.compact.uniq
216
+
217
+ all_packages.each do |pkg|
218
+ next if @backend.boards_installed?(pkg)
219
+
220
+ url = assure("Board package #{pkg} has a defined URL") { specific_config.package_url(pkg) }
221
+ @backend.board_manager_urls = [url]
222
+ assure("Installing board package #{pkg}") { @backend.install_boards(pkg) }
213
223
  end
214
- all_packages.each { |p| assure("Installing board package #{p}") { @backend.install_boards(p) } }
215
224
  end
216
225
 
217
226
  # @param expectation_envvar [String] the name of the env var to check
@@ -243,17 +252,25 @@ def handle_expectation_of_files(expectation_envvar, operation, filegroup_name, d
243
252
  end
244
253
 
245
254
  inform(problem) { dir_path }
255
+ explain_and_exercise_envvar(expectation_envvar, operation, "contents of #{dir_desc}") { display_files(dir) }
256
+ end
257
+
258
+ # @param expectation_envvar [String] the name of the env var to check
259
+ # @param operation [String] a description of what operation we might be skipping
260
+ # @param block_desc [String] a description of what information will be dumped to assist the user
261
+ # @param block [Proc] a function that dumps information
262
+ def explain_and_exercise_envvar(expectation_envvar, operation, block_desc, &block)
246
263
  inform("Environment variable #{expectation_envvar} is") { "(#{ENV[expectation_envvar].class}) #{ENV[expectation_envvar]}" }
247
264
  if ENV[expectation_envvar].nil?
248
265
  inform_multiline("Skipping #{operation}") do
249
- puts " In case that's an error, this is what was found in the #{dir_desc}:"
250
- display_files(dir)
266
+ puts " In case that's an error, displaying #{block_desc}:"
267
+ block.call
251
268
  puts " To force an error in this case, set the environment variable #{expectation_envvar}"
252
269
  true
253
270
  end
254
271
  else
255
- assure_multiline("Dumping project's #{dir_desc} before exit") do
256
- display_files(dir)
272
+ assure_multiline("Displaying #{block_desc} before exit") do
273
+ block.call
257
274
  false
258
275
  end
259
276
  end
@@ -277,6 +294,105 @@ def get_annotated_compilers(config, cpp_library)
277
294
  compilers
278
295
  end
279
296
 
297
+ # Handle existence or nonexistence of custom initialization script -- run it if you have it
298
+ #
299
+ # This feature is to drive GitHub actions / docker image installation where the container is
300
+ # in a clean-slate state but needs some way to have custom library versions injected into it.
301
+ # In this case, the user provided script would fetch a git repo or some other method
302
+ def perform_custom_initialization(_config)
303
+ script_path = ENV[VAR_CUSTOM_INIT_SCRIPT]
304
+ inform("Environment variable #{VAR_CUSTOM_INIT_SCRIPT}") { "'#{script_path}'" }
305
+ return if script_path.nil?
306
+ return if script_path.empty?
307
+
308
+ script_pathname = Pathname.getwd + script_path
309
+ assure("Script at #{VAR_CUSTOM_INIT_SCRIPT} exists") { script_pathname.exist? }
310
+
311
+ assure_multiline("Running #{script_pathname} with sh in libraries working dir") do
312
+ Dir.chdir(@backend.lib_dir) do
313
+ IO.popen(["/bin/sh", script_pathname.to_s], err: [:child, :out]) do |io|
314
+ io.each_line { |line| puts " #{line}" }
315
+ end
316
+ end
317
+ end
318
+ end
319
+
320
+ # Auto-select some platforms to test based on the information available
321
+ #
322
+ # Top choice is always library.properties -- otherwise use the default.
323
+ # But filter that through any non-default config
324
+ #
325
+ # @param config [CIConfig] the overridden config object
326
+ # @param reason [String] description of why we might use this platform (i.e. unittest or compilation)
327
+ # @param desired_platforms [Array<String>] the platform names specified
328
+ # @param library_properties [Hash] the library properties defined by the library
329
+ # @return [Array<String>] platforms to use
330
+ def choose_platform_set(config, reason, desired_platforms, library_properties)
331
+
332
+ # if there are no properties or no architectures, defer entirely to desired platforms
333
+ if library_properties.nil? || library_properties.architectures.nil? || library_properties.architectures.empty?
334
+ # verify that all platforms exist
335
+ desired_platforms.each { |p| assured_platform(reason, p, config) }
336
+ return inform_multiline("No architectures listed in library.properties, using configured platforms") do
337
+ desired_platforms.each { |p| puts " #{p}" } # this returns desired_platforms
338
+ end
339
+ end
340
+
341
+ if library_properties.architectures.include?("*")
342
+ return inform_multiline("Wildcard architecture in library.properties, using configured platforms") do
343
+ desired_platforms.each { |p| puts " #{p}" } # this returns desired_platforms
344
+ end
345
+ end
346
+
347
+ platform_architecture = config.platform_info.transform_values { |v| v[:board].split(":")[1] }
348
+ supported_platforms = platform_architecture.select { |_, a| library_properties.architectures.include?(a) }
349
+
350
+ if config.is_default
351
+ # completely ignore default config, opting for brute-force library matches
352
+ # OTOH, we don't need to assure platforms because we defined them
353
+ return inform_multiline("Default config, platforms matching architectures in library.properties") do
354
+ supported_platforms.each_key do |p|
355
+ puts " #{p}"
356
+ end # this returns supported_platforms
357
+ end
358
+ end
359
+
360
+ desired_supported_platforms = supported_platforms.select { |p, _| desired_platforms.include?(p) }.keys
361
+ desired_supported_platforms.each { |p| assured_platform(reason, p, config) }
362
+ inform_multiline("Configured platforms that match architectures in library.properties") do
363
+ desired_supported_platforms.each do |p|
364
+ puts " #{p}"
365
+ end # this returns supported_platforms
366
+ end
367
+ end
368
+
369
+ # tests of sane library.properties values
370
+ def perform_property_tests(cpp_library)
371
+ return inform("Skipping library.properties tests") { "as requested via command line" } if @cli_options[:skip_library_properties]
372
+ return inform("Skipping library.properties tests") { "as requested via environment" } unless ENV[VAR_SKIP_LIBPROPS].nil?
373
+ return inform("Skipping library.properties tests") { "file not found" } unless cpp_library.library_properties?
374
+
375
+ props = cpp_library.library_properties
376
+
377
+ props.depends&.each do |l|
378
+ assure("library.properties 'depends=' entry '#{l}' is available via the library manager") { @backend.library_available?(l) }
379
+ end
380
+
381
+ # the IDE would add these entries to a sketch (as "#include <...>" lines), they are nothing to do with the compioler
382
+ props.includes&.map(&:strip)&.map(&Pathname::method(:new))&.each do |f|
383
+ if (cpp_library.path + f).exist?
384
+ inform("library.properties 'includes=' entry found") { f }
385
+ elsif (cpp_library.path + "src" + f).exist?
386
+ inform("library.properties 'includes=' entry found") { Pathname.new("src") + f }
387
+ else
388
+ # this is if they want to "#include <math>" or something -- may or may not be valid! so just warn.
389
+ warn("library.properties 'includes=' entry '#{f}' does not refer to a file in the library")
390
+ end
391
+ end
392
+
393
+ end
394
+
395
+ # Unit test procedure
280
396
  def perform_unit_tests(cpp_library, file_config)
281
397
  if @cli_options[:skip_unittests]
282
398
  inform("Skipping unit tests") { "as requested via command line" }
@@ -285,7 +401,6 @@ def perform_unit_tests(cpp_library, file_config)
285
401
 
286
402
  config = file_config.with_override_config(@cli_options[:ci_config])
287
403
  compilers = get_annotated_compilers(config, cpp_library)
288
- config.platforms_to_unittest.each_with_object({}) { |p, acc| acc[p] = assured_platform("unittest", p, config) }
289
404
 
290
405
  inform("Library conforms to Arduino library specification") { cpp_library.one_point_five? ? "1.5" : "1.0" }
291
406
 
@@ -295,15 +410,20 @@ def perform_unit_tests(cpp_library, file_config)
295
410
  return
296
411
  end
297
412
 
298
- # Handle lack of platforms
299
- if config.platforms_to_unittest.empty?
300
- inform("Skipping unit tests") { "no platforms were requested" }
301
- return
413
+ # Get platforms, handle lack of them
414
+ platforms = choose_platform_set(config, "unittest", config.platforms_to_unittest, cpp_library.library_properties)
415
+ if platforms.empty?
416
+ explain_and_exercise_envvar(VAR_EXPECT_UNITTESTS, "unit tests", "platforms and architectures") do
417
+ puts " Configured platforms: #{config.platforms_to_unittest}"
418
+ puts " Configuration is default: #{config.is_default}"
419
+ arches = cpp_library.library_properties.nil? ? nil : cpp_library.library_properties.architectures
420
+ puts " Architectures in library.properties: #{arches}"
421
+ end
302
422
  end
303
423
 
304
424
  install_arduino_library_dependencies(config.aux_libraries_for_unittest, "<unittest/libraries>")
305
425
 
306
- config.platforms_to_unittest.each do |p|
426
+ platforms.each do |p|
307
427
  config.allowable_unittest_files(cpp_library.test_files).each do |unittest_path|
308
428
  unittest_name = unittest_path.basename.to_s
309
429
  compilers.each do |gcc_binary|
@@ -334,47 +454,33 @@ def perform_example_compilation_tests(cpp_library, config)
334
454
  return
335
455
  end
336
456
 
337
- # gather up all required boards for compilation so we can install them up front.
338
- # start with the "platforms to unittest" and add the examples
339
- # while we're doing that, get the aux libraries as well
340
- example_platform_info = {}
341
- board_package_url = {}
342
- aux_libraries = Set.new(config.aux_libraries_for_build)
343
- # while collecting the platforms, ensure they're defined
344
-
345
457
  library_examples = cpp_library.example_sketches
346
- library_examples.each do |path|
347
- ovr_config = config.from_example(path)
348
- ovr_config.platforms_to_build.each do |platform|
349
- # assure the platform if we haven't already
350
- next if example_platform_info.key?(platform)
351
-
352
- platform_info = assured_platform("library example", platform, config)
353
- next if platform_info.nil?
354
-
355
- example_platform_info[platform] = platform_info
356
- package = platform_info[:package]
357
- board_package_url[package] = ovr_config.package_url(package)
358
- end
359
- aux_libraries.merge(ovr_config.aux_libraries_for_build)
360
- end
361
-
362
- install_all_packages(example_platform_info, board_package_url)
363
- install_arduino_library_dependencies(aux_libraries, "<compile/libraries>")
364
458
 
365
- if config.platforms_to_build.empty?
366
- inform("Skipping builds") { "no platforms were requested" }
367
- return
368
- elsif library_examples.empty?
459
+ if library_examples.empty?
369
460
  handle_expectation_of_files(VAR_EXPECT_EXAMPLES, "builds", "examples", "the examples directory", cpp_library.examples_dir)
370
461
  return
371
462
  end
372
463
 
373
464
  library_examples.each do |example_path|
465
+ example_name = File.basename(example_path)
374
466
  ovr_config = config.from_example(example_path)
375
- ovr_config.platforms_to_build.each do |p|
376
- board = example_platform_info[p][:board]
377
- example_name = File.basename(example_path)
467
+ platforms = choose_platform_set(ovr_config, "library example", ovr_config.platforms_to_build, cpp_library.library_properties)
468
+
469
+ if platforms.empty?
470
+ explain_and_exercise_envvar(VAR_EXPECT_EXAMPLES, "examples compilation", "platforms and architectures") do
471
+ puts " Configured platforms: #{config.platforms_to_build}"
472
+ puts " Configuration is default: #{config.is_default}"
473
+ arches = cpp_library.library_properties.nil? ? nil : cpp_library.library_properties.architectures
474
+ puts " Architectures in library.properties: #{arches}"
475
+ end
476
+ end
477
+
478
+ install_all_packages(platforms, ovr_config)
479
+
480
+ platforms.each do |p|
481
+ install_arduino_library_dependencies(ovr_config.aux_libraries_for_build, "<compile/libraries>")
482
+
483
+ board = ovr_config.platform_info[p][:board]
378
484
  attempt("Compiling #{example_name} for #{board}") do
379
485
  ret = @backend.compile_sketch(example_path, board)
380
486
  unless ret
@@ -390,21 +496,23 @@ end
390
496
 
391
497
  # initialize command and config
392
498
  config = ArduinoCI::CIConfig.default.from_project_library
393
-
394
499
  @backend = ArduinoCI::ArduinoInstallation.autolocate!
395
500
  inform("Located arduino-cli binary") { @backend.binary_path.to_s }
396
501
 
502
+ # run any library init scripts from the library itself.
503
+ perform_custom_initialization(config)
504
+
397
505
  # initialize library under test
398
- cpp_library_path = Pathname.new(".")
506
+ inform("Environment variable #{VAR_USE_SUBDIR}") { "'#{ENV[VAR_USE_SUBDIR]}'" }
507
+ cpp_library_path = Pathname.new(ENV[VAR_USE_SUBDIR].nil? ? "." : ENV[VAR_USE_SUBDIR])
399
508
  cpp_library = assure("Installing library under test") do
400
509
  @backend.install_local_library(cpp_library_path)
401
510
  end
402
511
 
512
+ # Warn if the library name isn't obvious
403
513
  assumed_name = @backend.name_of_library(cpp_library_path)
404
- ondisk_name = cpp_library_path.realpath.basename
405
- if assumed_name != ondisk_name
406
- inform("WARNING") { "Installed library named '#{assumed_name}' has directory name '#{ondisk_name}'" }
407
- end
514
+ ondisk_name = cpp_library_path.realpath.basename.to_s
515
+ warn("Installed library named '#{assumed_name}' has directory name '#{ondisk_name}'") if assumed_name != ondisk_name
408
516
 
409
517
  if !cpp_library.nil?
410
518
  inform("Library installed at") { cpp_library.path.to_s }
@@ -416,6 +524,8 @@ else
416
524
  end
417
525
  end
418
526
 
527
+ perform_property_tests(cpp_library)
528
+
419
529
  install_arduino_library_dependencies(
420
530
  cpp_library.arduino_library_dependencies,
421
531
  "<#{ArduinoCI::CppLibrary::LIBRARY_PROPERTIES_FILE}>"