arduino_ci 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8f6fbc6b2113cea4a9188991098986ce101f0314
4
- data.tar.gz: 9239e52f0d098c9da5d0cec667aa2cb88239ed37
3
+ metadata.gz: 38db4872f12c8a48cbdf7033fba9b09ad530e140
4
+ data.tar.gz: 558c406c46bc3365bfaecfb9bef40155f0a95514
5
5
  SHA512:
6
- metadata.gz: 7d9953fd77ffbf8e209f5bd671b8002b86b3b2e43538b452540dd2e102a4792bf4316782c0370a0ea5afcef728224314e4ea293a1ab9aaa4c0b2f5dd0b63b7cc
7
- data.tar.gz: a2e241d167a95618398a9d9ce71f31bf8ad6473211de709aa7812d9b981f484fb5d2d02f08d0598edf35695ec6bce4416ce2cd13bfd6d94d9c5476b305cbc40e
6
+ metadata.gz: 68ef58a63df5eb220ba7c5a27833db522bef6f15fa77bc71d5533392d5af5c8bf11337ce88d9c69ba112327db4176a25971c25aaf115c17ddd2be7093753e3ec
7
+ data.tar.gz: 32460f132b22f151825590e6dfb845ec36a0a1058ed72b730987b3772db08e01291201c029d473cc2020d6721c55f0d0745fd7e178bb529c2239cbb177c94a2e
data/README.md CHANGED
@@ -1,28 +1,31 @@
1
1
 
2
- # ArduinoCI Ruby gem (`arduino_ci`) [![Gem Version](https://badge.fury.io/rb/arduino_ci.svg)](https://rubygems.org/gems/arduino_ci) [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://www.rubydoc.info/gems/arduino_ci/0.3.0)
2
+ # ArduinoCI Ruby gem (`arduino_ci`)
3
+ [![Gem Version](https://badge.fury.io/rb/arduino_ci.svg)](https://rubygems.org/gems/arduino_ci)
4
+ [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://www.rubydoc.info/gems/arduino_ci/0.4.0)
5
+ [![Gitter](https://badges.gitter.im/Arduino-CI/arduino_ci.svg)](https://gitter.im/Arduino-CI/arduino_ci?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
3
6
 
4
7
  You want to run tests on your Arduino library (bonus: without hardware present), but the IDE doesn't support that. Arduino CI provides that ability.
5
8
 
6
9
  You want to precisely replicate certain software states in your library, but you don't have sub-millisecond reflexes for physically faking the inputs, outputs, and serial port. Arduino CI fakes 100% of the physical input and output of an Arduino board, including the clock.
7
10
 
8
- You want your Arduino library to be automatically built and tested every time someone contributes code to your project on GitHub, but the Arduino IDE lacks the ability to run unit tests. [Arduino CI](https://github.com/ianfixes/arduino_ci) provides that ability.
11
+ You want your Arduino library to be automatically built and tested every time someone contributes code to your project on GitHub, but the Arduino IDE lacks the ability to run unit tests. [Arduino CI](https://github.com/Arduino-CI/arduino_ci) provides that ability.
9
12
 
10
13
  `arduino_ci` is a cross-platform build/test system, consisting of a Ruby gem and a series of C++ mocks. It enables tests to be run both locally and as part of a CI service like Travis or Appveyor. Any OS that can run the Arduino IDE can run `arduino_ci`.
11
14
 
12
15
  Platform | CI Status
13
16
  ---------|:---------
14
- OSX | [![OSX Build Status](http://badges.herokuapp.com/travis/ianfixes/arduino_ci?env=BADGE=osx&label=build&branch=master)](https://travis-ci.org/ianfixes/arduino_ci)
15
- Linux | [![Linux Build Status](http://badges.herokuapp.com/travis/ianfixes/arduino_ci?env=BADGE=linux&label=build&branch=master)](https://travis-ci.org/ianfixes/arduino_ci)
16
- Windows | [![Windows Build status](https://ci.appveyor.com/api/projects/status/8f6e39dea319m83q/branch/master?svg=true)](https://ci.appveyor.com/project/ianfixes/arduino-ci)
17
+ OSX | [![OSX Build Status](http://badges.herokuapp.com/travis/Arduino-CI/arduino_ci?env=BADGE=osx&label=build&branch=master)](https://travis-ci.org/Arduino-CI/arduino_ci)
18
+ Linux | [![Linux Build Status](http://badges.herokuapp.com/travis/Arduino-CI/arduino_ci?env=BADGE=linux&label=build&branch=master)](https://travis-ci.org/Arduino-CI/arduino_ci)
19
+ Windows | [![Windows Build status](https://ci.appveyor.com/api/projects/status/abynv8xd75m26qo9/branch/master?svg=true)](https://ci.appveyor.com/project/ianfixes/arduino-ci)
17
20
 
18
21
 
19
22
  ## Comparison to Other Arduino Testing Tools
20
23
 
21
24
  | Project | CI | Builds Examples | Unittest | Arduino Mocks | Windows | OSX | Linux | License |
22
25
  |-----------------------------------------------------------------------------|:--:|:---------------:|:--------:|:-------------:|:-------:|:---:|:-----:|:--------|
23
- |[ArduinoCI](https://github.com/ianfixes/arduino_ci) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |Free (Apache-2.0)|
26
+ |[ArduinoCI](https://github.com/Arduino-CI/arduino_ci) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |Free (Apache-2.0)|
24
27
  |[ArduinoUnit](https://github.com/mmurdoch/arduinounit) | ❌ | ❌ | ⚠️ Hardware-based|❌ | ✅ | ✅ | ✅ |Free (MIT)|
25
- |[Adafruit `travis-ci-arduino`](https://github.com/adafruit/travis-ci-arduino)| ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |Free (MIT)|
28
+ |[Adafruit `ci-arduino`](https://github.com/adafruit/ci-arduino)| ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |Free (MIT)|
26
29
  |[PlatformIO](https://platformio.org) | ✅ | ✅ | ⚠️ Paid only | ❌ | ✅ | ✅ | ✅ |⚠️ EULA|
27
30
  |Official [Arduino IDE](https://www.arduino.cc/en/main/software) | ❌ | ⚠️ Manually | ❌ |N/A 😉| ✅ | ✅ | ✅ |Free (GPLv2)|
28
31
 
@@ -82,10 +85,10 @@ vendor
82
85
 
83
86
  ### Installing the Dependencies
84
87
 
85
- Fulfilling the `arduino_ci` library dependency is as easy as running this command:
86
-
88
+ Fulfilling the `arduino_ci` library dependency is as easy as running either of these two commands:
87
89
  ```
88
- $ bundle install
90
+ $ bundle install # adds packages to global library (may require admin rights)
91
+ $ bundle install --path vendor/bundle # adds packages to local library
89
92
  ```
90
93
 
91
94
 
@@ -94,15 +97,15 @@ $ bundle install
94
97
  With that installed, just the following shell command each time you want the tests to execute:
95
98
 
96
99
  ```
97
- $ bundle exec arduino_ci_remote.rb
100
+ $ bundle exec arduino_ci.rb
98
101
  ```
99
102
 
100
- `arduino_ci_remote.rb` is the main entry point for this library. This command will iterate over all the library's `examples/` and attempt to compile them. If you set up unit tests, it will run those as well.
103
+ `arduino_ci.rb` is the main entry point for this library. This command will iterate over all the library's `examples/` and attempt to compile them. If you set up unit tests, it will run those as well.
101
104
 
102
105
 
103
106
  ### Reference
104
107
 
105
- For more information on the usage of `arduino_ci_remote.rb`, see [REFERENCE.md](REFERENCE.md). It contains information such as:
108
+ For more information on the usage of `arduino_ci.rb`, see [REFERENCE.md](REFERENCE.md). It contains information such as:
106
109
 
107
110
  * How to configure build options (platforms to test, Arduino library dependencies to install) with an `.arduino-ci.yml` file
108
111
  * Where to put unit test files
@@ -121,7 +124,7 @@ The following prerequisites must be fulfilled:
121
124
 
122
125
  ### Testing with remote CI
123
126
 
124
- > **Note:** `arduino_ci_remote.rb` expects to be run from the root directory of your Arduino project library.
127
+ > **Note:** `arduino_ci.rb` expects to be run from the root directory of your Arduino project library.
125
128
 
126
129
 
127
130
  #### Travis CI
@@ -135,7 +138,7 @@ sudo: false
135
138
  language: ruby
136
139
  script:
137
140
  - bundle install
138
- - bundle exec arduino_ci_remote.rb
141
+ - bundle exec arduino_ci.rb
139
142
  ```
140
143
 
141
144
 
@@ -149,14 +152,14 @@ Next, you'll need this in `appveyor.yml` in your repo.
149
152
  build: off
150
153
  test_script:
151
154
  - bundle install
152
- - bundle exec arduino_ci_remote.rb
155
+ - bundle exec arduino_ci.rb
153
156
  ```
154
157
 
155
158
  ## Known Problems
156
159
 
157
160
  * The Arduino library is not fully mocked.
158
161
  * I don't have preprocessor defines for all the Arduino board flavors
159
- * https://github.com/ianfixes/arduino_ci/issues
162
+ * https://github.com/Arduino-CI/arduino_ci/issues
160
163
 
161
164
 
162
165
  ## Author
@@ -167,6 +170,5 @@ This gem was written by Ian Katz (ianfixes@gmail.com) in 2018. It's released un
167
170
  ## See Also
168
171
 
169
172
  * [Contributing](CONTRIBUTING.md)
170
- * [Adafruit/travis-ci-arduino](https://github.com/adafruit/travis-ci-arduino) which inspired this project
173
+ * [Adafruit/ci-arduino](https://github.com/adafruit/ci-arduino) which inspired this project
171
174
  * [mmurdoch/arduinounit](https://github.com/mmurdoch/arduinounit) from which the unit test macros were adopted
172
-
@@ -0,0 +1,625 @@
1
+ # Build / Test Behavior of Arduino CI
2
+
3
+ All tests are run via the same command: `bundle exec arduino_ci.rb`.
4
+
5
+ This script is responsible for detecting and runing all unit tests, on every combination of Arduino platform and C++ compiler. This is followed by attempting to detect and build every example on every "default" Arduino platform.
6
+
7
+ As a prerequisite, all Arduino "default" platforms are installed if they are not already available.
8
+
9
+ These defaults are specified in [misc/default.yml](misc/default.yml). You are free to define new platforms and different compilers as you see fit, using your own project-specific overrides.
10
+
11
+
12
+ ## Directly Overriding Build Behavior (short term use)
13
+
14
+ When testing locally, it's often advantageous to limit the number of tests that are performed to only those tests that relate to the work you're doing; you'll get a faster turnaround time in seeing the results. For a full listing, see `bundle exec arduino_ci.rb --help`.
15
+
16
+
17
+ ### `--skip-unittests` option
18
+
19
+ This completely skips the unit testing portion of the CI script.
20
+
21
+
22
+ ### `--skip-compilation` option (deprecated)
23
+
24
+ This completely skips the compilation tests (of library examples) portion of the CI script. It does not skip the compilation of unit tests.
25
+
26
+
27
+ ### `--skip-examples-compilation` option
28
+
29
+ This completely skips the compilation tests (of library examples) portion of the CI script. It does not skip the compilation of unit tests.
30
+
31
+
32
+ ### `--testfile-select` option
33
+
34
+ This allows a file (or glob) pattern to be executed in your tests directory, creating a whitelist of files to test. E.g. `--testfile-select=test_animal_*.cpp` would match `test_animal_cat.cpp` and `test_animal_dog.cpp` (testing only those) and not `test_plant_rose.cpp`.
35
+
36
+ ### `--testfile-reject` option
37
+
38
+ This allows a file (or glob) pattern to be executed in your tests directory, creating a blacklist of files to skip. E.g. `--testfile-reject=test_animal_*.cpp` would match `test_animal_cat.cpp` and `test_animal_dog.cpp` (skipping those) and test only `test_plant_rose.cpp`, `test_plant_daisy.cpp`, etc.
39
+
40
+
41
+ ## Indirectly Overriding Build Behavior (medium term use), and Advanced Options
42
+
43
+ For build behavior that you'd like to persist across commits (e.g. defining the set of platforms to test against, disabling a test that you expect to re-enable at some future point), a special configuration file called `.arduino-ci.yml` can be used. There are 3 places you can put them:
44
+
45
+ 1. the root of your library
46
+ 2. the `test/` directory
47
+ 3. a subdirectory of `examples/`
48
+
49
+ `.arduino-ci.yml` files in `test/` or an example sketch take section-by-section precedence over a file in the library root, which takes precedence over the default configuration.
50
+
51
+
52
+ ### Defining New Arduino Platforms
53
+
54
+ Arduino boards are typically named in the form `manufacturer:family:model`. These definitions are not arbitrary -- they are defined in an Arduino _package_. For all but the built-in packages, you will need a package URL. Here is Adafruit's: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json
55
+
56
+ Here is how you would declare a package that includes the `potato:salad` family of boards in your `.arduino-ci.yml`:
57
+
58
+ ```yaml
59
+ packages:
60
+ potato:salad:
61
+ url: https://potato.github.io/arduino-board-index/package_salad_index.json
62
+ ```
63
+
64
+ To define a platform called `bogo` that uses a board called `potato:salad:bogo` (based on the `potato:salad` family), set it up in the `plaforms:` section. Note that this will override any default configuration of `bogo` if it had existed in `arduino_ci`'s `misc/default.yml` file. If this board defines particular features in the compiler, you can set those here.
65
+
66
+ ```yaml
67
+ platforms:
68
+ # our custom definition of the "bogo" platform
69
+ bogo:
70
+ board: potato:salad:bogo
71
+ package: potato:salad
72
+ gcc:
73
+ features:
74
+ - omit-frame-pointer # becomes -fomit-frame-pointer flag
75
+ defines:
76
+ - HAVE_THING # becomes -DHAVE_THING flag
77
+ warnings:
78
+ - no-implicit # becomes -Wno-implicit flag
79
+ flags:
80
+ - -foobar # becomes -foobar flag
81
+
82
+ # overriding the `zero` platform, to remove it completely
83
+ zero: ~
84
+
85
+ # redefine the existing esp8266
86
+ esp8266:
87
+ board: esp8266:esp8266:booo
88
+ package: esp8266:esp8266
89
+ gcc:
90
+ features:
91
+ defines:
92
+ warnings:
93
+ flags:
94
+ ```
95
+
96
+ ### Control How Examples Are Compiled
97
+
98
+ Put a file `.arduino-ci.yml` in each example directory where you require a different configuration than default.
99
+ The `compile:` section controls the platforms on which the compilation will be attempted, as well as any external libraries that must be installed and included.
100
+
101
+ ```yaml
102
+ compile:
103
+ # Choosing to run compilation tests on 2 different Arduino platforms
104
+ platforms:
105
+ - esp8266
106
+ - bogo
107
+
108
+ # Declaring Dependent Arduino Libraries (to be installed via the Arduino Library Manager)
109
+ libraries:
110
+ - "Adafruit FONA Library"
111
+ ```
112
+
113
+
114
+ ### Control How Unit Tests Are Compiled and Run
115
+
116
+ For your unit tests, in addition to setting specific libraries and platforms, you may filter the list of test files that are compiled and tested and choose additional compilers on which to run your tests.
117
+
118
+ Filtering your unit tests may help speed up targeted testing locally, but it is intended primarily as a means to temporarily disable tests between individual commits.
119
+
120
+ Furthermore, you can filter the files that will be included in the compilation step by specifying `exclude_dirs`. All cpp and header files in those directories will not be included in the compilation step, before the unittests are run.
121
+
122
+ ```yaml
123
+ unittest:
124
+
125
+ # Exclude these directories from compilation
126
+ exclude_dirs:
127
+ - someDirectory
128
+ - someOtherDirectory
129
+
130
+ # Perform unit tests with these compilers (these are the binaries that will be called via the shell)
131
+ compilers:
132
+ - g++ # default
133
+ - g++-4.9
134
+ - g++-7
135
+
136
+ # Filter the list of test files in some way
137
+ testfiles:
138
+ # files matching this glob (executed inside the `test/` directory) will be whitelisted for testing
139
+ select:
140
+ - "*-*.*"
141
+
142
+ # files matching this glob will be blacklisted from testing
143
+ reject:
144
+ - "sam-squamsh.*"
145
+
146
+ # These dependent libraries will be installed
147
+ libraries:
148
+ - "abc123"
149
+ - "def456"
150
+
151
+ # each of these platforms will be used when compiling the unit tests
152
+ platforms:
153
+ - bogo
154
+ ```
155
+
156
+ The expected number of tests will be the product of:
157
+
158
+ * Number of compilers defined
159
+ * Number of platforms defined
160
+ * Number of matching test files
161
+
162
+
163
+ ## Writing Unit tests in `test/`
164
+
165
+ All `.cpp` files in the `test/` directory of your Arduino library are assumed to contain unit tests. Each and every one will be compiled and executed on its own.
166
+
167
+
168
+ ### Most Basic Unit Test
169
+
170
+ The most basic unit test file is as follows:
171
+
172
+ ```C++
173
+ #include <ArduinoUnitTests.h>
174
+ #include "../do-something.h"
175
+
176
+ unittest(your_test_name)
177
+ {
178
+ assertEqual(4, doSomething());
179
+ }
180
+
181
+ unittest_main()
182
+ ```
183
+
184
+ This test defines one `unittest` (a macro provided by `ArduinoUnitTests.h`), called `your_test_name`, which makes some assertions on the target library. The `unittest_main()` is a macro for the `int main()` boilerplate required for unit testing.
185
+
186
+ ### Assertions
187
+
188
+ The following assertion functions are available in unit tests.
189
+
190
+ * `assertEqual(expected, actual)`
191
+ * `assertNotEqual(expected, actual)`
192
+ * `assertLess(expected, actual)`
193
+ * `assertMore(expected, actual)`
194
+ * `assertLessOrEqual(expected, actual)`
195
+ * `assertMoreOrEqual(expected, actual)`
196
+ * `assertTrue(actual)`
197
+ * `assertFalse(actual)`
198
+ * `assertNull(actual)`
199
+
200
+ These functions will report the result of the test to the console, and the testing will continue if they fail.
201
+
202
+ **If a test failure indicates that all subsequent tests will also fail** then it might be wiser to use _assure_ instead of _assert_ (e.g. `assureEqual(1, myVal)`). All of the above "assert" functions has a corresponding "assure" function; if the result is failure, the remaining tests in the unit test file are not run.
203
+
204
+
205
+ ### Test Setup and Teardown
206
+
207
+ For steps that are common to all tests, setup and teardown functions may optionally be supplied.
208
+
209
+ ```C++
210
+ #include <ArduinoUnitTests.h>
211
+
212
+ int* myNumber;
213
+
214
+ unittest_setup()
215
+ {
216
+ myNumber = new int(4);
217
+ }
218
+
219
+ unittest_teardown()
220
+ {
221
+ delete myNumber;
222
+ myNumber = NULL;
223
+ }
224
+
225
+ unittest(your_test_name)
226
+ {
227
+ assertEqual(4, *myNumber);
228
+ }
229
+
230
+ unittest_main()
231
+ ```
232
+
233
+
234
+ # Build Scripts
235
+
236
+ For most build environments, the only script that need be executed by the CI system is
237
+
238
+ ```shell
239
+ # simplest build script
240
+ bundle install
241
+ bundle exec arduino_ci.rb
242
+ ```
243
+
244
+ However, more flexible usage is available:
245
+
246
+ ### Custom Versions of external Arduino Libraries
247
+
248
+ Sometimes you need a fork of an Arduino library instead of the version that will be installed via their GUI. `arduino_ci.rb` won't overwrite existing downloaded libraries with fresh downloads, but it won't fetch the custom versions for you either.
249
+
250
+ If this is the behavior you need, `ensure_arduino_installation.rb` is for you. It ensures that an Arduino binary is available on the system.
251
+
252
+ ```shell
253
+ # Example build script
254
+ bundle install
255
+
256
+ # ensure the Arduino installation -- creates the Library directory
257
+ bundle exec ensure_arduino_installation.rb
258
+
259
+ # manually install a custom library from a zip file
260
+ wget https://hosting.com/custom_library.zip
261
+ unzip -o custom_library.zip
262
+ mv custom_library $(bundle exec arduino_library_location.rb)
263
+
264
+ # manually install a custom library from a git repository
265
+ git clone https://repository.com/custom_library_repo.git
266
+ mv custom_library_repo $(bundle exec arduino_library_location.rb)
267
+
268
+ # now run CI
269
+ bundle exec arduino_ci.rb
270
+ ```
271
+
272
+ Note the use of subshell to execute `bundle exec arduino_library_location.rb`. This command simply returns the directory in which Arduino Libraries are (or should be) installed.
273
+
274
+
275
+
276
+ # Mocks of Arduino Hardware Functions
277
+
278
+ Unless your library peforms something general (e.g. a mathematical or string function, a data structure like Queue, etc), you may need to ensure that your code interacts properly with the Arduino hardware. There are a series of mocks to assist in this.
279
+
280
+ ## Using `GODMODE`
281
+
282
+ Complete control of the Arduino environment is available in your unit tests through a construct called `GODMODE()`.
283
+
284
+ ```C++
285
+ unittest(example_godmode_stuff)
286
+ {
287
+ GodmodeState* state = GODMODE(); // get access to the state
288
+ state->reset(); // does a full reset of the state.
289
+ state->resetClock(); // - you can reset just the clock (to zero)
290
+ state->resetPins(); // - or just the pins
291
+ state->micros = 1; // manually set the clock such that micros() returns 1
292
+ state->digitalPin[4]; // tells you the commanded state of digital pin 4
293
+ state->digitalPin[4] = HIGH; // digitalRead(4) will now return HIGH
294
+ state->analogPin[3]; // tells you the commanded state of analog pin 3
295
+ state->analogPin[3] = 99; // analogRead(3) will now return 99
296
+ }
297
+ ```
298
+
299
+ ### Pin Histories
300
+
301
+ Of course, it's possible that your code might flip the bit more than once in a function. For that scenario, you may want to examine the history of a pin's commanded outputs:
302
+
303
+ ```C++
304
+ unittest(pin_history)
305
+ {
306
+ GodmodeState* state = GODMODE();
307
+ int myPin = 3;
308
+ state->reset(); // pin will start LOW
309
+ digitalWrite(myPin, HIGH);
310
+ digitalWrite(myPin, LOW);
311
+ digitalWrite(myPin, LOW);
312
+ digitalWrite(myPin, HIGH);
313
+ digitalWrite(myPin, HIGH);
314
+
315
+ // pin history is queued in case we want to analyze it later.
316
+ // we expect 6 values in that queue (5 that we set plus one
317
+ // initial value), which we'll hard-code here for convenience.
318
+ // (we'll actually assert those 6 values in the next block)
319
+ assertEqual(6, state->digitalPin[1].queueSize));
320
+ bool expected[6] = {LOW, HIGH, LOW, LOW, HIGH, HIGH};
321
+ bool actual[6];
322
+
323
+ // convert history queue into an array so we can verify it.
324
+ // while we're at it, check that we received the amount of
325
+ // elements that we expected.
326
+ int numMoved = state->digitalPin[myPin].toArray(actual, 6);
327
+ assertEqual(6, numMoved);
328
+
329
+ // verify each element
330
+ for (int i = 0; i < 6; ++i) {
331
+ assertEqual(expected[i], actual[i]);
332
+ }
333
+ }
334
+ ```
335
+
336
+
337
+ ### Pin Futures
338
+
339
+ Reading the pin more than once per function is also a possibility. In that case, we want to queue up a few values for the `digitalRead` or `analogRead` to find.
340
+
341
+ ```C++
342
+ unittest(pin_read_history)
343
+ {
344
+ GodmodeState* state = GODMODE();
345
+ state->reset();
346
+
347
+ int future[6] = {33, 22, 55, 11, 44, 66};
348
+ state->analogPin[1].fromArray(future, 6);
349
+ for (int i = 0; i < 6; ++i)
350
+ {
351
+ assertEqual(future[i], analogRead(1));
352
+ }
353
+
354
+ // for digital pins, we have the added possibility of specifying
355
+ // a stream of input bytes encoded as ASCII
356
+ bool bigEndian = true;
357
+ state->digitalPin[1].fromAscii("Yo", bigEndian);
358
+
359
+ // digitial history as serial data, big-endian
360
+ bool expectedBits[16] = {
361
+ 0, 1, 0, 1, 1, 0, 0, 1, // Y
362
+ 0, 1, 1, 0, 1, 1, 1, 1 // o
363
+ };
364
+
365
+ for (int i = 0; i < 16; ++i) {
366
+ assertEqual(expectedBits[i], digitalRead(1));
367
+ }
368
+ }
369
+ ```
370
+
371
+ ### Serial Data
372
+
373
+ Basic input and output verification of serial port data can be done as follows:
374
+
375
+ ```c++
376
+ unittest(reading_writing_serial)
377
+ {
378
+ GodmodeState* state = GODMODE();
379
+ state->serialPort[0].dataIn = ""; // the queue of data waiting to be read
380
+ state->serialPort[0].dataOut = ""; // the history of data written
381
+
382
+ // When there is no data, nothing happens
383
+ assertEqual(-1, Serial.peek());
384
+ assertEqual("", state->serialPort[0].dataIn);
385
+ assertEqual("", state->serialPort[0].dataOut);
386
+
387
+ // if we put data on the input and peek at it, we see the value and it's not consumed
388
+ state->serialPort[0].dataIn = "a";
389
+ assertEqual('a', Serial.peek());
390
+ assertEqual("a", state->serialPort[0].dataIn);
391
+ assertEqual("", state->serialPort[0].dataOut);
392
+
393
+ // if we read the input, we see the value and it's consumed
394
+ assertEqual('a', Serial.read());
395
+ assertEqual("", state->serialPort[0].dataIn);
396
+ assertEqual("", state->serialPort[0].dataOut);
397
+
398
+ // when we write data, it shows up in the history -- the output buffer
399
+ Serial.write('b');
400
+ assertEqual("", state->serialPort[0].dataIn);
401
+ assertEqual("b", state->serialPort[0].dataOut);
402
+
403
+ // when we print more data, note that the history
404
+ // still contains the first thing we wrote
405
+ Serial.print("cdefg");
406
+ assertEqual("", state->serialPort[0].dataIn);
407
+ assertEqual("bcdefg", state->serialPort[0].dataOut);
408
+ }
409
+ ```
410
+
411
+ A more complicated example: working with serial port IO. Let's say I have the following function:
412
+
413
+ ```C++
414
+ void smartLightswitchSerialHandler(int pin) {
415
+ if (Serial.available() > 0) {
416
+ int incomingByte = Serial.read();
417
+ int val = incomingByte == '0' ? LOW : HIGH;
418
+ Serial.print("Ack ");
419
+ digitalWrite(pin, val);
420
+ Serial.print(String(pin));
421
+ Serial.print(" ");
422
+ Serial.print((char)incomingByte);
423
+ }
424
+ }
425
+ ```
426
+
427
+ This function has 3 side effects: it drains the serial port's receive buffer, affects a pin, and puts data in the serial port's send buffer. Or, if the receive buffer is empty, it does nothing at all.
428
+
429
+ ```C++
430
+ unittest(does_nothing_if_no_data)
431
+ {
432
+ // configure initial state
433
+ GodmodeState* state = GODMODE();
434
+ int myPin = 3;
435
+ state->serialPort[0].dataIn = "";
436
+ state->serialPort[0].dataOut = "";
437
+ state->digitalPin[myPin] = LOW;
438
+
439
+ // execute action
440
+ smartLightswitchSerialHandler(myPin);
441
+
442
+ // assess final state
443
+ assertEqual(LOW, state->digitalPin[myPin]);
444
+ assertEqual("", state->serialPort[0].dataIn);
445
+ assertEqual("", state->serialPort[0].dataOut);
446
+ }
447
+
448
+ unittest(two_flips)
449
+ {
450
+ GodmodeState* state = GODMODE();
451
+ int myPin = 3;
452
+ state->serialPort[0].dataIn = "10junk";
453
+ state->serialPort[0].dataOut = "";
454
+ state->digitalPin[myPin] = LOW;
455
+ smartLightswitchSerialHandler(myPin);
456
+ assertEqual(HIGH, state->digitalPin[myPin]);
457
+ assertEqual("0junk", state->serialPort[0].dataIn);
458
+ assertEqual("Ack 3 1", state->serialPort[0].dataOut);
459
+
460
+ state->serialPort[0].dataOut = "";
461
+ smartLightswitchSerialHandler(myPin);
462
+ assertEqual(LOW, state->digitalPin[myPin]);
463
+ assertEqual("junk", state->serialPort[0].dataIn);
464
+ assertEqual("Ack 3 0", state->serialPort[0].dataOut);
465
+ }
466
+ ```
467
+
468
+ ### Pin History as ASCII
469
+
470
+
471
+ For additional complexity, there are some cases where you want to use a pin as a serial port. There are history functions for that too.
472
+
473
+ ```C++
474
+ int myPin = 3;
475
+
476
+ // digitial history as serial data, big-endian
477
+ bool bigEndian = true;
478
+ bool binaryAscii[24] = {
479
+ 0, 1, 0, 1, 1, 0, 0, 1, // Y
480
+ 0, 1, 1, 0, 0, 1, 0, 1, // e
481
+ 0, 1, 1, 1, 0, 0, 1, 1 // s
482
+ };
483
+
484
+ // "send" these bits
485
+ for (int i = 0; i < 24; digitalWrite(myPin, binaryAscii[i++]));
486
+
487
+ // The first bit in the history is the initial value, which we will ignore
488
+ int offset = 1;
489
+
490
+ // We should be able to parse the bits as ascii
491
+ assertEqual("Yes", state->digitalPin[myPin].toAscii(offset, bigEndian));
492
+ ```
493
+
494
+ Instead of queueing bits as ASCII for future use with `toAscii`, you can send those bits directly (and immediately) to the output using `outgoingFromAscii`. Likewise, you can reinterpret/examine (as ASCII) the bits you have previously queued up by calling `incomingToAscii` on the PinHistory object.
495
+
496
+
497
+ ### Interactivity of "Devices" with Observers
498
+
499
+ Even pin history and input/output buffers aren't capable of testing interactive code. For example, queueing the canned responses from a serial device before the requests are even sent to it is not a sane test environment; the library under test will see the entire future waiting for it on the input pin instead of a buffer that fills and empties over time. This calls for something more complicated.
500
+
501
+ In this example, we create a simple class to emulate a Hayes modem. (For more information, dig into the `DataStreamObserver` code on which `DeviceUsingBytes` is based.
502
+
503
+ ```c++
504
+ class FakeHayesModem : public DeviceUsingBytes {
505
+ public:
506
+ String mLast;
507
+
508
+ FakeHayesModem() : DeviceUsingBytes() {
509
+ mLast = "";
510
+ addResponseLine("AT", "OK");
511
+ addResponseLine("ATV1", "NO CARRIER");
512
+ }
513
+ virtual ~FakeHayesModem() {}
514
+ virtual void onMatchInput(String output) { mLast = output; }
515
+ };
516
+
517
+ unittest(modem_hardware)
518
+ {
519
+ GodmodeState* state = GODMODE();
520
+ state->reset();
521
+ FakeHayesModem m;
522
+ m.attach(&Serial);
523
+
524
+ Serial.write("AT\n");
525
+ assertEqual("AT\n", state->serialPort[0].dataOut);
526
+ assertEqual("OK\n", m.mLast);
527
+ }
528
+ ```
529
+
530
+ Note that instead of setting `mLast = output` in the `onMatchInput()` function for test purposes, we could just as easily queue some bytes to state->serialPort[0].dataIn for the library under test to find on its next `peek()` or `read()`. Or we could execute some action on a digital or analog input pin; the possibilities are fairly endless in this regard, although you will have to define them yourself -- from scratch -- extending the `DataStreamObserver` class to emulate your physical device.
531
+
532
+
533
+ ### Interrupts
534
+
535
+ Although ISRs should be tested directly (as their asynchronous nature is not mocked), the act of attaching or detaching an interrupt can be measured.
536
+
537
+ ```C++
538
+ unittest(interrupt_attachment) {
539
+ GodmodeState *state = GODMODE();
540
+ state->reset();
541
+ assertFalse(state->interrupt[7].attached);
542
+ attachInterrupt(7, (void (*)(void))0, 3);
543
+ assertTrue(state->interrupt[7].attached);
544
+ assertEqual(state->interrupt[7].mode, 3);
545
+ detachInterrupt(7);
546
+ assertFalse(state->interrupt[7].attached);
547
+ }
548
+ ```
549
+
550
+
551
+ ### SPI
552
+
553
+ These basic mocks of SPI store the values in Strings.
554
+
555
+ ```C++
556
+ unittest(spi) {
557
+ GodmodeState *state = GODMODE();
558
+
559
+ // 8-bit
560
+ state->reset();
561
+ state->spi.dataIn = "LMNO";
562
+ uint8_t out8 = SPI.transfer('a');
563
+ assertEqual("a", state->spi.dataOut);
564
+ assertEqual('L', out8);
565
+ assertEqual("MNO", state->spi.dataIn);
566
+
567
+ // 16-bit
568
+ union { uint16_t val; struct { char lsb; char msb; }; } in16, out16;
569
+ state->reset();
570
+ state->spi.dataIn = "LMNO";
571
+ in16.lsb = 'a';
572
+ in16.msb = 'b';
573
+ out16.val = SPI.transfer16(in16.val);
574
+ assertEqual("NO", state->spi.dataIn);
575
+ assertEqual('L', out16.lsb);
576
+ assertEqual('M', out16.msb);
577
+ assertEqual("ab", state->spi.dataOut);
578
+
579
+ // buffer
580
+ state->reset();
581
+ state->spi.dataIn = "LMNOP";
582
+ char inBuf[6] = "abcde";
583
+ SPI.transfer(inBuf, 4);
584
+
585
+ assertEqual("abcd", state->spi.dataOut);
586
+ assertEqual("LMNOe", String(inBuf));
587
+ }
588
+ ```
589
+
590
+ ### EEPROM
591
+
592
+ `EEPROM` is a global with a simple API to read and write bytes to persistent memory (like a tiny hard disk) given an `int` location. Since the Arduino core already provides this as a global, and the core API is sufficient for basic testing (read/write), there is no direct tie to the `GODMODE` API. (If you need more, such as a log of intermediate values, enter a feature request.)
593
+
594
+ ```C++
595
+ unittest(eeprom)
596
+ {
597
+ uint8_t a;
598
+ // size
599
+ assertEqual(EEPROM_SIZE, EEPROM.length());
600
+ // initial values
601
+ a = EEPROM.read(0);
602
+ assertEqual(255, a);
603
+ // write and read
604
+ EEPROM.write(0, 24);
605
+ a = EEPROM.read(0);
606
+ assertEqual(24, a);
607
+ // update
608
+ EEPROM.write(1, 14);
609
+ EEPROM.update(1, 22);
610
+ a = EEPROM.read(1);
611
+ assertEqual(22, a);
612
+ // put and get
613
+ const float f1 = 0.025f;
614
+ float f2 = 0.0f;
615
+ EEPROM.put(5, f1);
616
+ assertEqual(0.0f, f2);
617
+ EEPROM.get(5, f2);
618
+ assertEqual(0.025f, f2);
619
+ // array access
620
+ int val = 10;
621
+ EEPROM[2] = val;
622
+ a = EEPROM[2];
623
+ assertEqual(10, a);
624
+ }
625
+ ```