arduino_ci 0.2.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +49 -20
  3. data/REFERENCE.md +636 -0
  4. data/cpp/arduino/Arduino.h +1 -1
  5. data/cpp/arduino/AvrMath.h +117 -17
  6. data/cpp/arduino/Client.h +27 -0
  7. data/cpp/arduino/EEPROM.h +64 -0
  8. data/cpp/arduino/Godmode.cpp +11 -0
  9. data/cpp/arduino/Godmode.h +58 -9
  10. data/cpp/arduino/HardwareSerial.h +9 -28
  11. data/cpp/arduino/IPAddress.h +59 -0
  12. data/cpp/arduino/Print.h +9 -12
  13. data/cpp/arduino/Printable.h +8 -0
  14. data/cpp/arduino/SPI.h +11 -3
  15. data/cpp/arduino/Server.h +5 -0
  16. data/cpp/arduino/Udp.h +27 -0
  17. data/cpp/arduino/Wire.h +234 -0
  18. data/cpp/arduino/avr/io.h +10 -1
  19. data/cpp/arduino/avr/pgmspace.h +76 -46
  20. data/cpp/arduino/ci/StreamTape.h +36 -0
  21. data/cpp/unittest/OstreamHelpers.h +4 -0
  22. data/exe/arduino_ci.rb +427 -0
  23. data/exe/arduino_ci_remote.rb +2 -385
  24. data/exe/arduino_library_location.rb +2 -2
  25. data/exe/ensure_arduino_installation.rb +7 -1
  26. data/lib/arduino_ci.rb +1 -0
  27. data/lib/arduino_ci/arduino_backend.rb +222 -0
  28. data/lib/arduino_ci/arduino_downloader.rb +43 -73
  29. data/lib/arduino_ci/arduino_downloader_linux.rb +17 -55
  30. data/lib/arduino_ci/arduino_downloader_osx.rb +21 -33
  31. data/lib/arduino_ci/arduino_downloader_windows.rb +11 -53
  32. data/lib/arduino_ci/arduino_installation.rb +18 -80
  33. data/lib/arduino_ci/ci_config.rb +15 -9
  34. data/lib/arduino_ci/cpp_library.rb +266 -48
  35. data/lib/arduino_ci/host.rb +59 -4
  36. data/lib/arduino_ci/library_properties.rb +96 -0
  37. data/lib/arduino_ci/version.rb +1 -1
  38. data/misc/default.yml +55 -4
  39. metadata +21 -87
  40. data/cpp/arduino/Arduino.h.orig +0 -143
  41. data/exe/libasan.rb +0 -29
  42. data/lib/arduino_ci/arduino_cmd.rb +0 -328
  43. data/lib/arduino_ci/arduino_cmd_linux.rb +0 -17
  44. data/lib/arduino_ci/arduino_cmd_linux_builder.rb +0 -19
  45. data/lib/arduino_ci/arduino_cmd_osx.rb +0 -17
  46. data/lib/arduino_ci/arduino_cmd_windows.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ec22bfa6969f75cdb38ffa4365c3d8f2f83f2185
4
- data.tar.gz: 2380ce41fe0779e258471b28e9e94eee3c238369
2
+ SHA256:
3
+ metadata.gz: 6dab88af65d6e71fd9cd3e2f389340b184fb1fbb063ccb7a6743f893c47f690d
4
+ data.tar.gz: 267b58186eea10a1f52ff4c16838270437a7d52d8fd0a3681f84e770845efa0d
5
5
  SHA512:
6
- metadata.gz: 2c464279ef51852b51b22287960474da51a2818c9d41b8aadc181287636fa095effa8644f08320a62af7316a8540e9279ccf904c4936da619e82b7fb0011b21c
7
- data.tar.gz: 7dfecd48948dfa8588e150841ee24c472480a20f10892c6098905fa35220bca26690708f827ec5b290d1564006f5a3c3019c7638eac27b9669311f6203f3973c
6
+ metadata.gz: 8765d7d3c01fb5a4f9737604ee9ee33a020629e3e5d404403069ac346caad36f69a94893ca2db49cdc14bb50a1d5557d29cccd9b58022fb3d1a00cd9de348323
7
+ data.tar.gz: e537d36bd505b31630aae9e7359a71f82dbb07977bac93dbc4194d6da4112ca005e22b2e9051970d0d67afd4e5dcdabcd9a6dddda1ee399c0a7cfc01aecb631c
data/README.md CHANGED
@@ -1,28 +1,32 @@
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.2.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/1.1.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.
12
+
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 GitHub Actions, TravisCI, Appveyor, etc. Any OS that can run the Arduino IDE can run `arduino_ci`.
9
14
 
10
- `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
15
 
12
16
  Platform | CI Status
13
17
  ---------|:---------
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)
18
+ OSX | [![OSX Build Status](https://github.com/Arduino-CI/arduino_ci/workflows/macos/badge.svg)](https://github.com/Arduino-CI/arduino_ci/actions?workflow=macos)
19
+ Linux | [![Linux Build Status](https://github.com/Arduino-CI/arduino_ci/workflows/linux/badge.svg)](https://github.com/Arduino-CI/arduino_ci/actions?workflow=linux)
20
+ Windows | [![Windows Build status](https://github.com/Arduino-CI/arduino_ci/workflows/windows/badge.svg)](https://github.com/Arduino-CI/arduino_ci/actions?workflow=windows)
17
21
 
18
22
 
19
23
  ## Comparison to Other Arduino Testing Tools
20
24
 
21
25
  | Project | CI | Builds Examples | Unittest | Arduino Mocks | Windows | OSX | Linux | License |
22
26
  |-----------------------------------------------------------------------------|:--:|:---------------:|:--------:|:-------------:|:-------:|:---:|:-----:|:--------|
23
- |[ArduinoCI](https://github.com/ianfixes/arduino_ci) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |Free (Apache-2.0)|
27
+ |[ArduinoCI](https://github.com/Arduino-CI/arduino_ci) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |Free (Apache-2.0)|
24
28
  |[ArduinoUnit](https://github.com/mmurdoch/arduinounit) | ❌ | ❌ | ⚠️ Hardware-based|❌ | ✅ | ✅ | ✅ |Free (MIT)|
25
- |[Adafruit `travis-ci-arduino`](https://github.com/adafruit/travis-ci-arduino)| ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |Free (MIT)|
29
+ |[Adafruit `ci-arduino`](https://github.com/adafruit/ci-arduino)| ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |Free (MIT)|
26
30
  |[PlatformIO](https://platformio.org) | ✅ | ✅ | ⚠️ Paid only | ❌ | ✅ | ✅ | ✅ |⚠️ EULA|
27
31
  |Official [Arduino IDE](https://www.arduino.cc/en/main/software) | ❌ | ⚠️ Manually | ❌ |N/A 😉| ✅ | ✅ | ✅ |Free (GPLv2)|
28
32
 
@@ -33,6 +37,9 @@ For a bare-bones example that you can copy from, see [SampleProjects/DoSomething
33
37
 
34
38
  The complete set of C++ unit tests for the `arduino_ci` library itself are in the [SampleProjects/TestSomething](SampleProjects/TestSomething) project. The [test files](SampleProjects/TestSomething/test/) are named after the type of feature being tested.
35
39
 
40
+ > Arduino expects all libraries to be in a specific `Arduino/libraries` directory on your system. If your library is elsewhere, `arduino_ci` will _automatically_ create a symbolic link in the `libraries` directory that points to the directory of the project being tested. This simplifieds working with project dependencies, but **it can have unintended consequences on Windows systems** because [in some cases deleting a folder that contains a symbolic link to another folder can cause the _entire linked folder_ to be removed instead of just the link itself](https://superuser.com/a/306618).
41
+ >
42
+ > If you use a Windows system **it is recommended that you only run `arduino_ci` from project directories that are already inside the `libraries` directory**
36
43
 
37
44
  ### You Need Ruby and Bundler
38
45
 
@@ -82,10 +89,10 @@ vendor
82
89
 
83
90
  ### Installing the Dependencies
84
91
 
85
- Fulfilling the `arduino_ci` library dependency is as easy as running this command:
86
-
92
+ Fulfilling the `arduino_ci` library dependency is as easy as running either of these two commands:
87
93
  ```
88
- $ bundle install
94
+ $ bundle install # adds packages to global library (may require admin rights)
95
+ $ bundle install --path vendor/bundle # adds packages to local library
89
96
  ```
90
97
 
91
98
 
@@ -94,15 +101,15 @@ $ bundle install
94
101
  With that installed, just the following shell command each time you want the tests to execute:
95
102
 
96
103
  ```
97
- $ bundle exec arduino_ci_remote.rb
104
+ $ bundle exec arduino_ci.rb
98
105
  ```
99
106
 
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.
107
+ `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
108
 
102
109
 
103
110
  ### Reference
104
111
 
105
- For more information on the usage of `arduino_ci_remote.rb`, see [REFERENCE.md](REFERENCE.md). It contains information such as:
112
+ For more information on the usage of `arduino_ci.rb`, see [REFERENCE.md](REFERENCE.md). It contains information such as:
106
113
 
107
114
  * How to configure build options (platforms to test, Arduino library dependencies to install) with an `.arduino-ci.yml` file
108
115
  * Where to put unit test files
@@ -121,7 +128,29 @@ The following prerequisites must be fulfilled:
121
128
 
122
129
  ### Testing with remote CI
123
130
 
124
- > **Note:** `arduino_ci_remote.rb` expects to be run from the root directory of your Arduino project library.
131
+ > **Note:** `arduino_ci.rb` expects to be run from the root directory of your Arduino project library.
132
+
133
+
134
+ #### GitHub Actions
135
+
136
+ GitHub Actions allows you to automate your workflows directly in GitHub.
137
+ No additional steps are needed.
138
+ Just create a YAML file with the information below in your repo under the `.github/workflows/` directory.
139
+
140
+ ```yaml
141
+ on: [push, pull_request]
142
+ jobs:
143
+ runTest:
144
+ runs-on: ubuntu-latest
145
+ steps:
146
+ - uses: actions/checkout@v2
147
+ - uses: ruby/setup-ruby@v1
148
+ with:
149
+ ruby-version: 2.6
150
+ - run: |
151
+ bundle install
152
+ bundle exec arduino_ci_remote.rb
153
+ ```
125
154
 
126
155
 
127
156
  #### Travis CI
@@ -135,7 +164,7 @@ sudo: false
135
164
  language: ruby
136
165
  script:
137
166
  - bundle install
138
- - bundle exec arduino_ci_remote.rb
167
+ - bundle exec arduino_ci.rb
139
168
  ```
140
169
 
141
170
 
@@ -149,14 +178,15 @@ Next, you'll need this in `appveyor.yml` in your repo.
149
178
  build: off
150
179
  test_script:
151
180
  - bundle install
152
- - bundle exec arduino_ci_remote.rb
181
+ - bundle exec arduino_ci.rb
153
182
  ```
154
183
 
184
+
155
185
  ## Known Problems
156
186
 
157
187
  * The Arduino library is not fully mocked.
158
188
  * I don't have preprocessor defines for all the Arduino board flavors
159
- * https://github.com/ianfixes/arduino_ci/issues
189
+ * https://github.com/Arduino-CI/arduino_ci/issues
160
190
 
161
191
 
162
192
  ## Author
@@ -167,6 +197,5 @@ This gem was written by Ian Katz (ianfixes@gmail.com) in 2018. It's released un
167
197
  ## See Also
168
198
 
169
199
  * [Contributing](CONTRIBUTING.md)
170
- * [Adafruit/travis-ci-arduino](https://github.com/adafruit/travis-ci-arduino) which inspired this project
200
+ * [Adafruit/ci-arduino](https://github.com/adafruit/ci-arduino) which inspired this project
171
201
  * [mmurdoch/arduinounit](https://github.com/mmurdoch/arduinounit) from which the unit test macros were adopted
172
-
@@ -0,0 +1,636 @@
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
+
37
+ ### `--testfile-reject` option
38
+
39
+ 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.
40
+
41
+
42
+ ### `EXPECT_UNITTESTS` environment variable
43
+
44
+ If set, testing will fail if no unit test files are detected (or if the directory does not exist). This is to avoid communicating a passing status in cases where a commit may have accidentally moved or deleted the test files.
45
+
46
+
47
+ ### `EXPECT_EXAMPLES` environment variable
48
+
49
+ If set, testing will fail if no example sketches are detected. This is to avoid communicating a passing status in cases where a commit may have accidentally moved or deleted the examples.
50
+
51
+
52
+ ## Indirectly Overriding Build Behavior (medium term use), and Advanced Options
53
+
54
+ 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:
55
+
56
+ 1. the root of your library
57
+ 2. the `test/` directory
58
+ 3. a subdirectory of `examples/`
59
+
60
+ `.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.
61
+
62
+
63
+ ### Defining New Arduino Platforms
64
+
65
+ 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
66
+
67
+ Here is how you would declare a package that includes the `potato:salad` set of platforms (aka "board family") in your `.arduino-ci.yml`:
68
+
69
+ ```yaml
70
+ packages:
71
+ potato:salad:
72
+ url: https://potato.github.io/arduino-board-index/package_salad_index.json
73
+ ```
74
+
75
+ 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.
76
+
77
+ ```yaml
78
+ platforms:
79
+ # our custom definition of the "bogo" platform
80
+ bogo:
81
+ board: potato:salad:bogo
82
+ package: potato:salad
83
+ gcc:
84
+ features:
85
+ - omit-frame-pointer # becomes -fomit-frame-pointer flag
86
+ defines:
87
+ - HAVE_THING # becomes -DHAVE_THING flag
88
+ warnings:
89
+ - no-implicit # becomes -Wno-implicit flag
90
+ flags:
91
+ - -foobar # becomes -foobar flag
92
+
93
+ # overriding the `zero` platform, to remove it completely
94
+ zero: ~
95
+
96
+ # redefine the existing esp8266
97
+ esp8266:
98
+ board: esp8266:esp8266:booo
99
+ package: esp8266:esp8266
100
+ gcc:
101
+ features:
102
+ defines:
103
+ warnings:
104
+ flags:
105
+ ```
106
+
107
+ ### Control How Examples Are Compiled
108
+
109
+ Put a file `.arduino-ci.yml` in each example directory where you require a different configuration than default.
110
+ 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.
111
+
112
+ ```yaml
113
+ compile:
114
+ # Choosing to run compilation tests on 2 different Arduino platforms
115
+ platforms:
116
+ - esp8266
117
+ - bogo
118
+
119
+ # Declaring Dependent Arduino Libraries (to be installed via the Arduino Library Manager)
120
+ libraries:
121
+ - "Adafruit FONA Library"
122
+ ```
123
+
124
+
125
+ ### Control How Unit Tests Are Compiled and Run
126
+
127
+ 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.
128
+
129
+ 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.
130
+
131
+ 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.
132
+
133
+ ```yaml
134
+ unittest:
135
+
136
+ # Exclude these directories from compilation
137
+ exclude_dirs:
138
+ - someDirectory
139
+ - someOtherDirectory
140
+
141
+ # Perform unit tests with these compilers (these are the binaries that will be called via the shell)
142
+ compilers:
143
+ - g++ # default
144
+ - g++-4.9
145
+ - g++-7
146
+
147
+ # Filter the list of test files in some way
148
+ testfiles:
149
+ # files matching this glob (executed inside the `test/` directory) will be whitelisted for testing
150
+ select:
151
+ - "*-*.*"
152
+
153
+ # files matching this glob will be blacklisted from testing
154
+ reject:
155
+ - "sam-squamsh.*"
156
+
157
+ # These dependent libraries will be installed
158
+ libraries:
159
+ - "abc123"
160
+ - "def456"
161
+
162
+ # each of these platforms will be used when compiling the unit tests
163
+ platforms:
164
+ - bogo
165
+ ```
166
+
167
+ The expected number of tests will be the product of:
168
+
169
+ * Number of compilers defined
170
+ * Number of platforms defined
171
+ * Number of matching test files
172
+
173
+
174
+ ## Writing Unit tests in `test/`
175
+
176
+ 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.
177
+
178
+
179
+ ### Most Basic Unit Test
180
+
181
+ The most basic unit test file is as follows:
182
+
183
+ ```C++
184
+ #include <ArduinoUnitTests.h>
185
+ #include "../do-something.h"
186
+
187
+ unittest(your_test_name)
188
+ {
189
+ assertEqual(4, doSomething());
190
+ }
191
+
192
+ unittest_main()
193
+ ```
194
+
195
+ 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.
196
+
197
+ ### Assertions
198
+
199
+ The following assertion functions are available in unit tests.
200
+
201
+ * `assertEqual(expected, actual)`
202
+ * `assertNotEqual(expected, actual)`
203
+ * `assertLess(expected, actual)`
204
+ * `assertMore(expected, actual)`
205
+ * `assertLessOrEqual(expected, actual)`
206
+ * `assertMoreOrEqual(expected, actual)`
207
+ * `assertTrue(actual)`
208
+ * `assertFalse(actual)`
209
+ * `assertNull(actual)`
210
+
211
+ These functions will report the result of the test to the console, and the testing will continue if they fail.
212
+
213
+ **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.
214
+
215
+
216
+ ### Test Setup and Teardown
217
+
218
+ For steps that are common to all tests, setup and teardown functions may optionally be supplied.
219
+
220
+ ```C++
221
+ #include <ArduinoUnitTests.h>
222
+
223
+ int* myNumber;
224
+
225
+ unittest_setup()
226
+ {
227
+ myNumber = new int(4);
228
+ }
229
+
230
+ unittest_teardown()
231
+ {
232
+ delete myNumber;
233
+ myNumber = NULL;
234
+ }
235
+
236
+ unittest(your_test_name)
237
+ {
238
+ assertEqual(4, *myNumber);
239
+ }
240
+
241
+ unittest_main()
242
+ ```
243
+
244
+
245
+ # Build Scripts
246
+
247
+ For most build environments, the only script that need be executed by the CI system is
248
+
249
+ ```shell
250
+ # simplest build script
251
+ bundle install
252
+ bundle exec arduino_ci.rb
253
+ ```
254
+
255
+ However, more flexible usage is available:
256
+
257
+ ### Custom Versions of external Arduino Libraries
258
+
259
+ 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.
260
+
261
+ 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.
262
+
263
+ ```shell
264
+ # Example build script
265
+ bundle install
266
+
267
+ # ensure the Arduino installation -- creates the Library directory
268
+ bundle exec ensure_arduino_installation.rb
269
+
270
+ # manually install a custom library from a zip file
271
+ wget https://hosting.com/custom_library.zip
272
+ unzip -o custom_library.zip
273
+ mv custom_library $(bundle exec arduino_library_location.rb)
274
+
275
+ # manually install a custom library from a git repository
276
+ git clone https://repository.com/custom_library_repo.git
277
+ mv custom_library_repo $(bundle exec arduino_library_location.rb)
278
+
279
+ # now run CI
280
+ bundle exec arduino_ci.rb
281
+ ```
282
+
283
+ 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.
284
+
285
+
286
+
287
+ # Mocks of Arduino Hardware Functions
288
+
289
+ 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.
290
+
291
+ ## Using `GODMODE`
292
+
293
+ Complete control of the Arduino environment is available in your unit tests through a construct called `GODMODE()`.
294
+
295
+ ```C++
296
+ unittest(example_godmode_stuff)
297
+ {
298
+ GodmodeState* state = GODMODE(); // get access to the state
299
+ state->reset(); // does a full reset of the state.
300
+ state->resetClock(); // - you can reset just the clock (to zero)
301
+ state->resetPins(); // - or just the pins
302
+ state->micros = 1; // manually set the clock such that micros() returns 1
303
+ state->digitalPin[4]; // tells you the commanded state of digital pin 4
304
+ state->digitalPin[4] = HIGH; // digitalRead(4) will now return HIGH
305
+ state->analogPin[3]; // tells you the commanded state of analog pin 3
306
+ state->analogPin[3] = 99; // analogRead(3) will now return 99
307
+ }
308
+ ```
309
+
310
+ ### Pin Histories
311
+
312
+ 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:
313
+
314
+ ```C++
315
+ unittest(pin_history)
316
+ {
317
+ GodmodeState* state = GODMODE();
318
+ int myPin = 3;
319
+ state->reset(); // pin will start LOW
320
+ digitalWrite(myPin, HIGH);
321
+ digitalWrite(myPin, LOW);
322
+ digitalWrite(myPin, LOW);
323
+ digitalWrite(myPin, HIGH);
324
+ digitalWrite(myPin, HIGH);
325
+
326
+ // pin history is queued in case we want to analyze it later.
327
+ // we expect 6 values in that queue (5 that we set plus one
328
+ // initial value), which we'll hard-code here for convenience.
329
+ // (we'll actually assert those 6 values in the next block)
330
+ assertEqual(6, state->digitalPin[1].queueSize));
331
+ bool expected[6] = {LOW, HIGH, LOW, LOW, HIGH, HIGH};
332
+ bool actual[6];
333
+
334
+ // convert history queue into an array so we can verify it.
335
+ // while we're at it, check that we received the amount of
336
+ // elements that we expected.
337
+ int numMoved = state->digitalPin[myPin].toArray(actual, 6);
338
+ assertEqual(6, numMoved);
339
+
340
+ // verify each element
341
+ for (int i = 0; i < 6; ++i) {
342
+ assertEqual(expected[i], actual[i]);
343
+ }
344
+ }
345
+ ```
346
+
347
+
348
+ ### Pin Futures
349
+
350
+ 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.
351
+
352
+ ```C++
353
+ unittest(pin_read_history)
354
+ {
355
+ GodmodeState* state = GODMODE();
356
+ state->reset();
357
+
358
+ int future[6] = {33, 22, 55, 11, 44, 66};
359
+ state->analogPin[1].fromArray(future, 6);
360
+ for (int i = 0; i < 6; ++i)
361
+ {
362
+ assertEqual(future[i], analogRead(1));
363
+ }
364
+
365
+ // for digital pins, we have the added possibility of specifying
366
+ // a stream of input bytes encoded as ASCII
367
+ bool bigEndian = true;
368
+ state->digitalPin[1].fromAscii("Yo", bigEndian);
369
+
370
+ // digitial history as serial data, big-endian
371
+ bool expectedBits[16] = {
372
+ 0, 1, 0, 1, 1, 0, 0, 1, // Y
373
+ 0, 1, 1, 0, 1, 1, 1, 1 // o
374
+ };
375
+
376
+ for (int i = 0; i < 16; ++i) {
377
+ assertEqual(expectedBits[i], digitalRead(1));
378
+ }
379
+ }
380
+ ```
381
+
382
+ ### Serial Data
383
+
384
+ Basic input and output verification of serial port data can be done as follows:
385
+
386
+ ```c++
387
+ unittest(reading_writing_serial)
388
+ {
389
+ GodmodeState* state = GODMODE();
390
+ state->serialPort[0].dataIn = ""; // the queue of data waiting to be read
391
+ state->serialPort[0].dataOut = ""; // the history of data written
392
+
393
+ // When there is no data, nothing happens
394
+ assertEqual(-1, Serial.peek());
395
+ assertEqual("", state->serialPort[0].dataIn);
396
+ assertEqual("", state->serialPort[0].dataOut);
397
+
398
+ // if we put data on the input and peek at it, we see the value and it's not consumed
399
+ state->serialPort[0].dataIn = "a";
400
+ assertEqual('a', Serial.peek());
401
+ assertEqual("a", state->serialPort[0].dataIn);
402
+ assertEqual("", state->serialPort[0].dataOut);
403
+
404
+ // if we read the input, we see the value and it's consumed
405
+ assertEqual('a', Serial.read());
406
+ assertEqual("", state->serialPort[0].dataIn);
407
+ assertEqual("", state->serialPort[0].dataOut);
408
+
409
+ // when we write data, it shows up in the history -- the output buffer
410
+ Serial.write('b');
411
+ assertEqual("", state->serialPort[0].dataIn);
412
+ assertEqual("b", state->serialPort[0].dataOut);
413
+
414
+ // when we print more data, note that the history
415
+ // still contains the first thing we wrote
416
+ Serial.print("cdefg");
417
+ assertEqual("", state->serialPort[0].dataIn);
418
+ assertEqual("bcdefg", state->serialPort[0].dataOut);
419
+ }
420
+ ```
421
+
422
+ A more complicated example: working with serial port IO. Let's say I have the following function:
423
+
424
+ ```C++
425
+ void smartLightswitchSerialHandler(int pin) {
426
+ if (Serial.available() > 0) {
427
+ int incomingByte = Serial.read();
428
+ int val = incomingByte == '0' ? LOW : HIGH;
429
+ Serial.print("Ack ");
430
+ digitalWrite(pin, val);
431
+ Serial.print(String(pin));
432
+ Serial.print(" ");
433
+ Serial.print((char)incomingByte);
434
+ }
435
+ }
436
+ ```
437
+
438
+ 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.
439
+
440
+ ```C++
441
+ unittest(does_nothing_if_no_data)
442
+ {
443
+ // configure initial state
444
+ GodmodeState* state = GODMODE();
445
+ int myPin = 3;
446
+ state->serialPort[0].dataIn = "";
447
+ state->serialPort[0].dataOut = "";
448
+ state->digitalPin[myPin] = LOW;
449
+
450
+ // execute action
451
+ smartLightswitchSerialHandler(myPin);
452
+
453
+ // assess final state
454
+ assertEqual(LOW, state->digitalPin[myPin]);
455
+ assertEqual("", state->serialPort[0].dataIn);
456
+ assertEqual("", state->serialPort[0].dataOut);
457
+ }
458
+
459
+ unittest(two_flips)
460
+ {
461
+ GodmodeState* state = GODMODE();
462
+ int myPin = 3;
463
+ state->serialPort[0].dataIn = "10junk";
464
+ state->serialPort[0].dataOut = "";
465
+ state->digitalPin[myPin] = LOW;
466
+ smartLightswitchSerialHandler(myPin);
467
+ assertEqual(HIGH, state->digitalPin[myPin]);
468
+ assertEqual("0junk", state->serialPort[0].dataIn);
469
+ assertEqual("Ack 3 1", state->serialPort[0].dataOut);
470
+
471
+ state->serialPort[0].dataOut = "";
472
+ smartLightswitchSerialHandler(myPin);
473
+ assertEqual(LOW, state->digitalPin[myPin]);
474
+ assertEqual("junk", state->serialPort[0].dataIn);
475
+ assertEqual("Ack 3 0", state->serialPort[0].dataOut);
476
+ }
477
+ ```
478
+
479
+ ### Pin History as ASCII
480
+
481
+
482
+ 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.
483
+
484
+ ```C++
485
+ int myPin = 3;
486
+
487
+ // digitial history as serial data, big-endian
488
+ bool bigEndian = true;
489
+ bool binaryAscii[24] = {
490
+ 0, 1, 0, 1, 1, 0, 0, 1, // Y
491
+ 0, 1, 1, 0, 0, 1, 0, 1, // e
492
+ 0, 1, 1, 1, 0, 0, 1, 1 // s
493
+ };
494
+
495
+ // "send" these bits
496
+ for (int i = 0; i < 24; digitalWrite(myPin, binaryAscii[i++]));
497
+
498
+ // The first bit in the history is the initial value, which we will ignore
499
+ int offset = 1;
500
+
501
+ // We should be able to parse the bits as ascii
502
+ assertEqual("Yes", state->digitalPin[myPin].toAscii(offset, bigEndian));
503
+ ```
504
+
505
+ 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.
506
+
507
+
508
+ ### Interactivity of "Devices" with Observers
509
+
510
+ 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.
511
+
512
+ 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.
513
+
514
+ ```c++
515
+ class FakeHayesModem : public DeviceUsingBytes {
516
+ public:
517
+ String mLast;
518
+
519
+ FakeHayesModem() : DeviceUsingBytes() {
520
+ mLast = "";
521
+ addResponseLine("AT", "OK");
522
+ addResponseLine("ATV1", "NO CARRIER");
523
+ }
524
+ virtual ~FakeHayesModem() {}
525
+ virtual void onMatchInput(String output) { mLast = output; }
526
+ };
527
+
528
+ unittest(modem_hardware)
529
+ {
530
+ GodmodeState* state = GODMODE();
531
+ state->reset();
532
+ FakeHayesModem m;
533
+ m.attach(&Serial);
534
+
535
+ Serial.write("AT\n");
536
+ assertEqual("AT\n", state->serialPort[0].dataOut);
537
+ assertEqual("OK\n", m.mLast);
538
+ }
539
+ ```
540
+
541
+ 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.
542
+
543
+
544
+ ### Interrupts
545
+
546
+ Although ISRs should be tested directly (as their asynchronous nature is not mocked), the act of attaching or detaching an interrupt can be measured.
547
+
548
+ ```C++
549
+ unittest(interrupt_attachment) {
550
+ GodmodeState *state = GODMODE();
551
+ state->reset();
552
+ assertFalse(state->interrupt[7].attached);
553
+ attachInterrupt(7, (void (*)(void))0, 3);
554
+ assertTrue(state->interrupt[7].attached);
555
+ assertEqual(state->interrupt[7].mode, 3);
556
+ detachInterrupt(7);
557
+ assertFalse(state->interrupt[7].attached);
558
+ }
559
+ ```
560
+
561
+
562
+ ### SPI
563
+
564
+ These basic mocks of SPI store the values in Strings.
565
+
566
+ ```C++
567
+ unittest(spi) {
568
+ GodmodeState *state = GODMODE();
569
+
570
+ // 8-bit
571
+ state->reset();
572
+ state->spi.dataIn = "LMNO";
573
+ uint8_t out8 = SPI.transfer('a');
574
+ assertEqual("a", state->spi.dataOut);
575
+ assertEqual('L', out8);
576
+ assertEqual("MNO", state->spi.dataIn);
577
+
578
+ // 16-bit
579
+ union { uint16_t val; struct { char lsb; char msb; }; } in16, out16;
580
+ state->reset();
581
+ state->spi.dataIn = "LMNO";
582
+ in16.lsb = 'a';
583
+ in16.msb = 'b';
584
+ out16.val = SPI.transfer16(in16.val);
585
+ assertEqual("NO", state->spi.dataIn);
586
+ assertEqual('L', out16.lsb);
587
+ assertEqual('M', out16.msb);
588
+ assertEqual("ab", state->spi.dataOut);
589
+
590
+ // buffer
591
+ state->reset();
592
+ state->spi.dataIn = "LMNOP";
593
+ char inBuf[6] = "abcde";
594
+ SPI.transfer(inBuf, 4);
595
+
596
+ assertEqual("abcd", state->spi.dataOut);
597
+ assertEqual("LMNOe", String(inBuf));
598
+ }
599
+ ```
600
+
601
+ ### EEPROM
602
+
603
+ `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.)
604
+
605
+ ```C++
606
+ unittest(eeprom)
607
+ {
608
+ uint8_t a;
609
+ // size
610
+ assertEqual(EEPROM_SIZE, EEPROM.length());
611
+ // initial values
612
+ a = EEPROM.read(0);
613
+ assertEqual(255, a);
614
+ // write and read
615
+ EEPROM.write(0, 24);
616
+ a = EEPROM.read(0);
617
+ assertEqual(24, a);
618
+ // update
619
+ EEPROM.write(1, 14);
620
+ EEPROM.update(1, 22);
621
+ a = EEPROM.read(1);
622
+ assertEqual(22, a);
623
+ // put and get
624
+ const float f1 = 0.025f;
625
+ float f2 = 0.0f;
626
+ EEPROM.put(5, f1);
627
+ assertEqual(0.0f, f2);
628
+ EEPROM.get(5, f2);
629
+ assertEqual(0.025f, f2);
630
+ // array access
631
+ int val = 10;
632
+ EEPROM[2] = val;
633
+ a = EEPROM[2];
634
+ assertEqual(10, a);
635
+ }
636
+ ```