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.
- checksums.yaml +4 -4
- data/README.md +32 -24
- data/REFERENCE.md +91 -11
- data/cpp/arduino/Arduino.h +0 -5
- data/cpp/arduino/ArduinoDefines.h +3 -0
- data/cpp/arduino/Godmode.h +63 -6
- data/cpp/arduino/Wire.h +37 -13
- data/cpp/unittest/ArduinoUnitTests.h +32 -0
- data/cpp/unittest/Assertion.h +54 -26
- data/cpp/unittest/Compare.h +58 -51
- data/exe/arduino_ci.rb +186 -76
- data/lib/arduino_ci/arduino_backend.rb +18 -2
- data/lib/arduino_ci/ci_config.rb +5 -2
- data/lib/arduino_ci/host.rb +1 -1
- data/lib/arduino_ci/library_properties.rb +6 -1
- data/lib/arduino_ci/version.rb +1 -1
- data/misc/default.yml +2 -2
- metadata +2 -2
@@ -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,
|
data/cpp/unittest/Assertion.h
CHANGED
@@ -6,9 +6,19 @@
|
|
6
6
|
|
7
7
|
#include "Compare.h"
|
8
8
|
|
9
|
-
#define
|
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
|
26
|
-
|
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
|
29
|
-
|
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
|
34
|
-
#define
|
35
|
-
#define
|
36
|
-
#define
|
37
|
-
#define
|
38
|
-
#define
|
39
|
-
#define
|
40
|
-
#define
|
41
|
-
#define
|
42
|
-
#define
|
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
|
46
|
-
#define
|
47
|
-
#define
|
48
|
-
#define
|
49
|
-
#define
|
50
|
-
#define
|
51
|
-
#define
|
52
|
-
#define
|
53
|
-
#define
|
54
|
-
#define
|
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))
|
data/cpp/unittest/Compare.h
CHANGED
@@ -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)
|
14
|
-
inline static bool notEqual(const A &a,const B &b)
|
15
|
-
inline static bool
|
16
|
-
inline static bool
|
17
|
-
inline static bool
|
18
|
-
inline static bool
|
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
|
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(
|
66
|
-
inline static bool equal(
|
67
|
-
inline static bool notEqual(
|
68
|
-
inline static bool
|
69
|
-
inline static bool
|
70
|
-
inline static bool
|
71
|
-
inline static bool
|
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
|
-
|
75
|
-
|
79
|
+
eqComparisonTemplateMacro(String, , String, , a.compareTo(b))
|
80
|
+
eqComparisonTemplateMacro(String, , const char *, , a.compareTo(b))
|
76
81
|
#if defined(F)
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
-
|
103
|
-
|
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(
|
107
|
-
template <typename A, typename B> bool compareEqual(
|
108
|
-
template <typename A, typename B> bool compareNotEqual(
|
109
|
-
template <typename A, typename B> bool
|
110
|
-
template <typename A, typename B> bool
|
111
|
-
template <typename A, typename B> bool
|
112
|
-
template <typename A, typename B> bool
|
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); }
|
data/exe/arduino_ci.rb
CHANGED
@@ -5,8 +5,11 @@ require 'pathname'
|
|
5
5
|
require 'optparse'
|
6
6
|
|
7
7
|
WIDTH = 80
|
8
|
-
|
9
|
-
|
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
|
73
|
-
puts
|
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
|
194
|
-
# @param
|
195
|
-
def install_all_packages(
|
196
|
-
|
197
|
-
#
|
198
|
-
all_packages =
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
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,
|
250
|
-
|
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("
|
256
|
-
|
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
|
-
#
|
299
|
-
|
300
|
-
|
301
|
-
|
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
|
-
|
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
|
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.
|
376
|
-
|
377
|
-
|
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
|
-
|
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}>"
|