arduino_ci 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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}>"