arduino_ci 0.3.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +125 -69
  3. data/REFERENCE.md +711 -0
  4. data/cpp/arduino/Arduino.h +1 -7
  5. data/cpp/arduino/ArduinoDefines.h +3 -0
  6. data/cpp/arduino/AvrMath.h +117 -17
  7. data/cpp/arduino/Client.h +27 -0
  8. data/cpp/arduino/EEPROM.h +64 -0
  9. data/cpp/arduino/Godmode.cpp +7 -0
  10. data/cpp/arduino/Godmode.h +121 -15
  11. data/cpp/arduino/HardwareSerial.h +4 -4
  12. data/cpp/arduino/IPAddress.h +59 -0
  13. data/cpp/arduino/Print.h +9 -12
  14. data/cpp/arduino/Printable.h +8 -0
  15. data/cpp/arduino/SPI.h +11 -3
  16. data/cpp/arduino/Server.h +5 -0
  17. data/cpp/arduino/Udp.h +27 -0
  18. data/cpp/arduino/Wire.h +197 -77
  19. data/cpp/arduino/avr/io.h +10 -1
  20. data/cpp/arduino/avr/pgmspace.h +76 -46
  21. data/cpp/unittest/ArduinoUnitTests.h +32 -0
  22. data/cpp/unittest/Assertion.h +54 -26
  23. data/cpp/unittest/Compare.h +58 -51
  24. data/cpp/unittest/OstreamHelpers.h +4 -0
  25. data/exe/arduino_ci.rb +538 -0
  26. data/exe/arduino_ci_remote.rb +2 -393
  27. data/exe/arduino_library_location.rb +2 -2
  28. data/exe/ensure_arduino_installation.rb +7 -1
  29. data/lib/arduino_ci.rb +1 -0
  30. data/lib/arduino_ci/arduino_backend.rb +238 -0
  31. data/lib/arduino_ci/arduino_downloader.rb +43 -73
  32. data/lib/arduino_ci/arduino_downloader_linux.rb +17 -55
  33. data/lib/arduino_ci/arduino_downloader_osx.rb +21 -33
  34. data/lib/arduino_ci/arduino_downloader_windows.rb +11 -53
  35. data/lib/arduino_ci/arduino_installation.rb +18 -80
  36. data/lib/arduino_ci/ci_config.rb +8 -11
  37. data/lib/arduino_ci/cpp_library.rb +250 -59
  38. data/lib/arduino_ci/host.rb +59 -4
  39. data/lib/arduino_ci/library_properties.rb +101 -0
  40. data/lib/arduino_ci/version.rb +1 -1
  41. data/misc/default.yml +57 -6
  42. metadata +19 -87
  43. data/cpp/arduino/Arduino.h.orig +0 -143
  44. data/exe/libasan.rb +0 -29
  45. data/lib/arduino_ci/arduino_cmd.rb +0 -332
  46. data/lib/arduino_ci/arduino_cmd_linux.rb +0 -17
  47. data/lib/arduino_ci/arduino_cmd_linux_builder.rb +0 -19
  48. data/lib/arduino_ci/arduino_cmd_osx.rb +0 -17
  49. data/lib/arduino_ci/arduino_cmd_windows.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 8f6fbc6b2113cea4a9188991098986ce101f0314
4
- data.tar.gz: 9239e52f0d098c9da5d0cec667aa2cb88239ed37
2
+ SHA256:
3
+ metadata.gz: c4765de27f7f477f5a64189afba2d70a280d1ae65e64916af6b98aa7d1a0266f
4
+ data.tar.gz: dd3748bdf67dfc526183d5a1372ce06d11b293cbaf984af19b90b999fdf1623e
5
5
  SHA512:
6
- metadata.gz: 7d9953fd77ffbf8e209f5bd671b8002b86b3b2e43538b452540dd2e102a4792bf4316782c0370a0ea5afcef728224314e4ea293a1ab9aaa4c0b2f5dd0b63b7cc
7
- data.tar.gz: a2e241d167a95618398a9d9ce71f31bf8ad6473211de709aa7812d9b981f484fb5d2d02f08d0598edf35695ec6bce4416ce2cd13bfd6d94d9c5476b305cbc40e
6
+ metadata.gz: ae3f368ed4f543f22a0702df9945abac4df15683f85e7d54c5c958cc60b951453bd21aad19beac73a54257bee558ea172d5c8cbcea69deed3a7b15da0cff0495
7
+ data.tar.gz: b2481e5d6096e6dd6df1aa86efb6465e6f13dc853840fca4ba6044c8dc51b1ac1c8c0196f3c9d49c1412c4e880177a40880346e1bba1d4fb7cc4967468f192f7
data/README.md CHANGED
@@ -1,108 +1,146 @@
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/1.3.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)
6
+ [![GitHub Marketplace](https://img.shields.io/badge/Get_it-on_Marketplace-informational.svg)](https://github.com/marketplace/actions/arduino_ci)
3
7
 
4
- 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.
8
+ Arduino CI was created to enable better collaboration among Arduino library maintainers and contributors, by enabling automated code checks to be performed as part of a pull request process.
5
9
 
6
- 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.
10
+ * enables running unit tests against the library **without hardware present**
11
+ * provides a system of mocks that allow fine-grained control over the hardware inputs, including the system's clock
12
+ * verifies compilation of any example sketches included in the library
13
+ * can test a wide range of arduino boards with different hardware options available
14
+ * compares entries in `library.properties` to the contents of the library and reports mismatches
15
+ * can be run both locally and as part of CI (GitHub Actions, TravisCI, Appveyor, etc.)
16
+ * runs on multiple platforms -- any platform that supports the Arduino IDE
17
+ * provides detailed analysis of segfaults in compilers that support such debugging features
7
18
 
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.
19
+ > Note: for running tests in response to [GitHub events](https://docs.github.com/en/free-pro-team@latest/developers/webhooks-and-events/github-event-types), an [Arduino CI GitHub Action](https://github.com/marketplace/actions/arduino_ci) is available for your convenience. This method of running `arduino_ci` is driven by Docker, which may also serve your local testing needs (as it does not require a ruby environment to be installed).
9
20
 
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`.
21
+ Arduino CI works on multiple platforms, which should enable your CI system of choice to leverage it for testing.
11
22
 
12
23
  Platform | CI Status
13
24
  ---------|:---------
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)
25
+ 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)
26
+ 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)
27
+ 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
28
 
18
29
 
19
- ## Comparison to Other Arduino Testing Tools
30
+ ## Quick Start
20
31
 
21
- | Project | CI | Builds Examples | Unittest | Arduino Mocks | Windows | OSX | Linux | License |
22
- |-----------------------------------------------------------------------------|:--:|:---------------:|:--------:|:-------------:|:-------:|:---:|:-----:|:--------|
23
- |[ArduinoCI](https://github.com/ianfixes/arduino_ci) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |Free (Apache-2.0)|
24
- |[ArduinoUnit](https://github.com/mmurdoch/arduinounit) | ❌ | ❌ | ⚠️ Hardware-based|❌ | ✅ | ✅ | ✅ |Free (MIT)|
25
- |[Adafruit `travis-ci-arduino`](https://github.com/adafruit/travis-ci-arduino)| ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |Free (MIT)|
26
- |[PlatformIO](https://platformio.org) | ✅ | ✅ | ⚠️ Paid only | ❌ | ✅ | ✅ | ✅ |⚠️ EULA|
27
- |Official [Arduino IDE](https://www.arduino.cc/en/main/software) | ❌ | ⚠️ Manually | ❌ |N/A 😉| ✅ | ✅ | ✅ |Free (GPLv2)|
32
+ This project has the following dependencies:
28
33
 
34
+ * `ruby` 2.5 or higher
35
+ * A compiler like `g++` (on OSX, `clang` works; on Cygwin, use the `mingw-gcc-c++` package)
36
+ * `python` (if using a board architecutre that requires it, e.g. ESP32, ESP8266; see [this issue](https://github.com/Arduino-CI/arduino_ci/issues/235#issuecomment-739629243)). Consider `pyserial` as well.
29
37
 
30
- ## Quick Start
38
+ In that environment, you can install by running `gem install arduino_ci`. To update to a latest version, use `gem update arduino_ci`.
39
+
40
+ You can now test your library by simply running the command `arduino_ci.rb` from your library directory. This will perform the following:
41
+
42
+ * validation of some fields in `library.properties`, if it exists
43
+ * running unit tests from files found in `test/`, if they exist
44
+ * testing compilation of example sketches found in `examples/`, if they exist
45
+
46
+ ### Assumptions About Your Repository
47
+
48
+ 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**.
31
49
 
32
- For a bare-bones example that you can copy from, see [SampleProjects/DoSomething](SampleProjects/DoSomething).
50
+ > 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** 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).
33
51
 
34
- 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.
52
+ ### Changes to Your Repository
53
+
54
+ Unit testing binaries created by `arduino_ci` should not be commited to the codebase. To avoid that, add the following to your `.gitignore`:
55
+
56
+ ```ignore-list
57
+ # arduino_ci unit test binaries and artifacts
58
+ *.bin
59
+ *.bin.dSYM
60
+ ```
35
61
 
62
+ ### A Quick Example
36
63
 
37
- ### You Need Ruby and Bundler
64
+ For a fairly minimal practical example of a unit-testable library repo that you can copy from, see [the `Arduino-CI/Blink` repository](https://github.com/Arduino-CI/Blink).
38
65
 
39
- You'll need Ruby version 2.2 or higher, and to `gem install bundler` if it's not already there.
40
66
 
67
+ ## Advanced Start
41
68
 
42
- ### You Need A Compiler (`g++`)
69
+ New features and bugfixes reach GitHub before they reach a released ruby gem. Alternately, it may be that (for your own reasons) you do not wish to install `arduino_ci` globally on your system. A few additional setup steps are required if you wish to do this.
43
70
 
44
- For unit testing, you will need a compiler; [g++](https://gcc.gnu.org/) is preferred.
71
+ ### You Need Ruby _and_ Bundler
45
72
 
46
- * **Linux**: `gcc`/`g++` is likely pre-installed.
47
- * **OSX**: `g++` is an alias for `clang`, which is provided by Xcode and the developer tools. You are free to `brew install gcc` as well; this is also tested and working.
48
- * **Windows**: you will need Cygwin, and the `mingw-gcc-g++` package. A full set of (working) install instructions can be found in `appveyor.yml`, as this is how CI runs for this project.
73
+ In addition to version 2.5 or higher, you'll also need to `gem install bundler` to a minimum of version 2.0 if it's not already there. You may find it easiest to do this by using [`rbenv`](https://github.com/rbenv/rbenv).
49
74
 
75
+ You will need to add a file called `Gemfile` (no extension) to your Arduino project.
50
76
 
51
- ### Changes to Your Repo
77
+ #### Non-root installation
52
78
 
53
- Add a file called `Gemfile` (no extension) to your Arduino project:
79
+ If you are simply trying to avoid the need to install `arduino_ci` system-wide (which may require administrator permissions), your `Gemfile` would look like this:
54
80
 
55
81
  ```ruby
56
82
  source 'https://rubygems.org'
57
- gem 'arduino_ci'
58
- ```
59
-
60
- It would also make sense to add the following to your `.gitignore`, or copy [the `.gitignore` used by this project](.gitignore):
61
83
 
84
+ # Replace 1.2 with the desired version of arduino_ci. See https://guides.rubygems.org/patterns/#pessimistic-version-constraint
85
+ gem 'arduino_ci', '~> 1.2'
62
86
  ```
87
+
88
+ It would also make sense to add the following to your `.gitignore`:
89
+ ```ignore-list
63
90
  /.bundle/
64
- /.yardoc
65
- Gemfile.lock
66
- /_yardoc/
67
- /coverage/
68
- /doc/
69
- /pkg/
70
- /spec/reports/
71
91
  vendor
72
- *.gem
92
+ ```
73
93
 
74
- # rspec failure tracking
75
- .rspec_status
94
+ > Note: this used to be the recommended installation method, but with the library's maturation it's better to avoid the use of `Gemfile` and `bundle install` by just installing as per the "Quick Start" instructions above.
76
95
 
77
- # C++ stuff
78
- *.bin
79
- *.bin.dSYM
96
+
97
+ #### Using the latest-available code
98
+
99
+ If you want to use the latest code on GitHub, your `Gemfile` would look like this:
100
+
101
+ ```ruby
102
+ source 'https://rubygems.org'
103
+
104
+ # to use the latest github code in a given repo and branch, replace the below values for git: and ref: as needed
105
+ gem 'arduino_ci', git: 'https://github.com/ArduinoCI/arduino_ci.git', ref: '<your desired ref, branch, or tag>'
80
106
  ```
81
107
 
82
108
 
83
- ### Installing the Dependencies
109
+ #### Using a version of `arduino_ci` source code on your local machine
84
110
 
85
- Fulfilling the `arduino_ci` library dependency is as easy as running this command:
111
+ First, Thanks! See [CONTRIBUTING.md](CONTRIBUTING.md). Your `Gemfile` would look like this:
112
+
113
+ ```ruby
114
+ source 'https://rubygems.org'
115
+
116
+ gem 'arduino_ci', path: '/path/to/development/dir/for/arduino_ci'
117
+ ```
118
+
119
+
120
+ ### Installing the Dependencies
86
121
 
122
+ Fulfilling the `arduino_ci` library dependency is as easy as running either of these two commands:
87
123
  ```
88
- $ bundle install
124
+ $ bundle install # adds packages to global library (may require admin rights)
125
+ $ bundle install --path vendor/bundle # adds packages to local library
89
126
  ```
90
127
 
128
+ This will create a `Gemfile.lock` in your project directory, which you may optionally check into source control. A broader introduction to ruby dependencies is outside the scope of this document.
91
129
 
92
- ### Running tests
130
+
131
+
132
+ ### Running `arduino_ci.rb` To Test Your Library
93
133
 
94
134
  With that installed, just the following shell command each time you want the tests to execute:
95
135
 
96
- ```
97
- $ bundle exec arduino_ci_remote.rb
136
+ ```console
137
+ $ bundle exec arduino_ci.rb
98
138
  ```
99
139
 
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.
101
-
102
140
 
103
141
  ### Reference
104
142
 
105
- For more information on the usage of `arduino_ci_remote.rb`, see [REFERENCE.md](REFERENCE.md). It contains information such as:
143
+ For more information on the usage of `arduino_ci.rb`, see [REFERENCE.md](REFERENCE.md). It contains information such as:
106
144
 
107
145
  * How to configure build options (platforms to test, Arduino library dependencies to install) with an `.arduino-ci.yml` file
108
146
  * Where to put unit test files
@@ -113,18 +151,36 @@ For more information on the usage of `arduino_ci_remote.rb`, see [REFERENCE.md](
113
151
 
114
152
  ## Setting up Pull Request Testing and/or External CI
115
153
 
116
- The following prerequisites must be fulfilled:
154
+ > **Note:** `arduino_ci.rb` expects to be run from the root directory of your Arduino project library.
155
+
156
+ ### Arduino CI's Own GitHub action
117
157
 
118
- * A GitHub (or other repository-hosting) project for your library
119
- * A CI system like [Travis CI](https://travis-ci.org/) or [Appveyor](https://www.appveyor.com/) that is linked to your project
158
+ [![GitHub Marketplace](https://img.shields.io/badge/Get_it-on_Marketplace-informational.svg)](https://github.com/marketplace/actions/arduino_ci)
120
159
 
121
160
 
122
- ### Testing with remote CI
161
+ ### Your Own Scripted GitHub Action
123
162
 
124
- > **Note:** `arduino_ci_remote.rb` expects to be run from the root directory of your Arduino project library.
163
+ GitHub Actions allows you to automate your workflows directly in GitHub.
164
+ No additional steps are needed.
165
+ Just create a YAML file with the information below in your repo under the `.github/workflows/` directory.
166
+
167
+ ```yaml
168
+ on: [push, pull_request]
169
+ jobs:
170
+ runTest:
171
+ runs-on: ubuntu-latest
172
+ steps:
173
+ - uses: actions/checkout@v2
174
+ - uses: ruby/setup-ruby@v1
175
+ with:
176
+ ruby-version: 2.6
177
+ - run: |
178
+ gem install arduino_ci
179
+ arduino_ci.rb
180
+ ```
125
181
 
126
182
 
127
- #### Travis CI
183
+ ### Travis CI
128
184
 
129
185
  You'll need to go to https://travis-ci.org/profile/ and enable testing for your Arduino project. Once that happens, you should be all set. The script will test all example projects of the library and all unit tests.
130
186
 
@@ -134,12 +190,12 @@ Next, you need this in `.travis.yml` in your repo
134
190
  sudo: false
135
191
  language: ruby
136
192
  script:
137
- - bundle install
138
- - bundle exec arduino_ci_remote.rb
193
+ - gem install arduino_ci
194
+ - arduino_ci.rb
139
195
  ```
140
196
 
141
197
 
142
- #### Appveyor CI
198
+ ### Appveyor CI
143
199
 
144
200
  You'll need to go to https://ci.appveyor.com/projects and add your project.
145
201
 
@@ -148,15 +204,16 @@ Next, you'll need this in `appveyor.yml` in your repo.
148
204
  ```yaml
149
205
  build: off
150
206
  test_script:
151
- - bundle install
152
- - bundle exec arduino_ci_remote.rb
207
+ - gem install arduino_ci
208
+ - arduino_ci.rb
153
209
  ```
154
210
 
211
+
155
212
  ## Known Problems
156
213
 
157
- * The Arduino library is not fully mocked.
214
+ * The Arduino library is not fully mocked, nor is `avr-libc`.
158
215
  * I don't have preprocessor defines for all the Arduino board flavors
159
- * https://github.com/ianfixes/arduino_ci/issues
216
+ * https://github.com/Arduino-CI/arduino_ci/issues
160
217
 
161
218
 
162
219
  ## Author
@@ -167,6 +224,5 @@ This gem was written by Ian Katz (ianfixes@gmail.com) in 2018. It's released un
167
224
  ## See Also
168
225
 
169
226
  * [Contributing](CONTRIBUTING.md)
170
- * [Adafruit/travis-ci-arduino](https://github.com/adafruit/travis-ci-arduino) which inspired this project
227
+ * [Adafruit/ci-arduino](https://github.com/adafruit/ci-arduino) which inspired this project
171
228
  * [mmurdoch/arduinounit](https://github.com/mmurdoch/arduinounit) from which the unit test macros were adopted
172
-
@@ -0,0 +1,711 @@
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
+ ### `--skip-library-properties` option
33
+
34
+ This completely skips validation of entries in `library.properties`.
35
+
36
+
37
+ ### `--testfile-select` option
38
+
39
+ 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`.
40
+
41
+
42
+ ### `--testfile-reject` option
43
+
44
+ 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.
45
+
46
+
47
+ ### `CUSTOM_INIT_SCRIPT` environment variable
48
+
49
+ If set, testing will execute (using `/bin/sh`) the script referred to by this variable -- relative to the current working directory (i.e. the root directory of the library). The script will _run_ in the Arduino Libraries directory (changing to the Libraries directory, running the script, and returning to the individual library root afterward). This enables use cases like the GitHub action to install custom library versions (i.e. a version of a library that is different than what the library manager would automatically install by name) prior to CI test runs.
50
+
51
+
52
+ ### `USE_SUBDIR` environment variable
53
+
54
+ If set, testing will be conducted in this subdirectory (relative to the working directory). This is for monorepos or other layouts where the library directory and project root directory are different.
55
+
56
+
57
+ ### `EXPECT_UNITTESTS` environment variable
58
+
59
+ 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.
60
+
61
+
62
+ ### `EXPECT_EXAMPLES` environment variable
63
+
64
+ 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.
65
+
66
+
67
+ ## Indirectly Overriding Build Behavior (medium term use), and Advanced Options
68
+
69
+ 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:
70
+
71
+ 1. the root of your library
72
+ 2. the `test/` directory
73
+ 3. a subdirectory of `examples/`
74
+
75
+ `.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.
76
+
77
+
78
+ ### Defining New Arduino Platforms
79
+
80
+ 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
81
+
82
+ Here is how you would declare a package that includes the `potato:salad` set of platforms (aka "board family") in your `.arduino-ci.yml`:
83
+
84
+ ```yaml
85
+ packages:
86
+ potato:salad:
87
+ url: https://potato.github.io/arduino-board-index/package_salad_index.json
88
+ ```
89
+
90
+ 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.
91
+
92
+ > Note that the platform names are arbitrary -- just keys in this yaml file and in the [`default.yml`](https://github.com/Arduino-CI/arduino_ci/blob/master/misc/default.yml) file included in this gem. That said, they are also case sensitive; defining the `bogo` platform will not let you refer to it as `Bogo` nor `BOGO`.
93
+
94
+ ```yaml
95
+ platforms:
96
+ # our custom definition of the "bogo" platform
97
+ bogo:
98
+ board: potato:salad:bogo
99
+ package: potato:salad
100
+ gcc:
101
+ features:
102
+ - omit-frame-pointer # becomes -fomit-frame-pointer flag
103
+ defines:
104
+ - HAVE_THING # becomes -DHAVE_THING flag
105
+ warnings:
106
+ - no-implicit # becomes -Wno-implicit flag
107
+ flags:
108
+ - -foobar # becomes -foobar flag
109
+
110
+ # overriding the `zero` platform, to remove it completely
111
+ zero: ~
112
+
113
+ # redefine the existing esp8266
114
+ esp8266:
115
+ board: esp8266:esp8266:booo
116
+ package: esp8266:esp8266
117
+ gcc:
118
+ features:
119
+ defines:
120
+ warnings:
121
+ flags:
122
+ ```
123
+
124
+ ### Control How Examples Are Compiled
125
+
126
+ Put a file `.arduino-ci.yml` in each example directory where you require a different configuration than default.
127
+ 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. This works by _overriding_ portions of the default configuration.
128
+
129
+ > Note that the platform names _must_ match (case-sensitive) the platform names in the underlying [`default.yml`](https://github.com/Arduino-CI/arduino_ci/blob/master/misc/default.yml), or else match platforms that you have defined yourself in your `.arduino-ci.yml` override.
130
+
131
+ ```yaml
132
+ compile:
133
+ # Choosing to run compilation tests on 2 different Arduino platforms
134
+ platforms:
135
+ - esp8266
136
+ - bogo
137
+
138
+ # Declaring Dependent Arduino Libraries (to be installed via the Arduino Library Manager)
139
+ libraries:
140
+ - "Adafruit FONA Library"
141
+ ```
142
+
143
+
144
+ ### Control How Unit Tests Are Compiled and Run
145
+
146
+ 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.
147
+
148
+ 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.
149
+
150
+ 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.
151
+
152
+ ```yaml
153
+ unittest:
154
+
155
+ # Exclude these directories from compilation
156
+ exclude_dirs:
157
+ - someDirectory
158
+ - someOtherDirectory
159
+
160
+ # Perform unit tests with these compilers (these are the binaries that will be called via the shell)
161
+ compilers:
162
+ - g++ # default
163
+ - g++-4.9
164
+ - g++-7
165
+
166
+ # Filter the list of test files in some way
167
+ testfiles:
168
+ # files matching this glob (executed inside the `test/` directory) will be whitelisted for testing
169
+ select:
170
+ - "*-*.*"
171
+
172
+ # files matching this glob will be blacklisted from testing
173
+ reject:
174
+ - "sam-squamsh.*"
175
+
176
+ # These dependent libraries will be installed
177
+ libraries:
178
+ - "abc123"
179
+ - "def456"
180
+
181
+ # each of these platforms will be used when compiling the unit tests
182
+ platforms:
183
+ - bogo
184
+ ```
185
+
186
+ The expected number of tests will be the product of:
187
+
188
+ * Number of compilers defined
189
+ * Number of platforms defined
190
+ * Number of matching test files
191
+
192
+
193
+ ## Writing Unit tests in `test/`
194
+
195
+ 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.
196
+
197
+
198
+ ### Most Basic Unit Test
199
+
200
+ The most basic unit test file is as follows:
201
+
202
+ ```C++
203
+ #include <ArduinoUnitTests.h>
204
+ #include "../do-something.h"
205
+
206
+ unittest(your_test_name)
207
+ {
208
+ assertEqual(4, doSomething());
209
+ }
210
+
211
+ unittest_main()
212
+ ```
213
+
214
+ 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.
215
+
216
+ ### Assertions
217
+
218
+ The following assertion functions are available in unit tests.
219
+
220
+ ```c++
221
+ assertEqual(expected, actual); // a == b
222
+ assertNotEqual(unwanted, actual); // a != b
223
+ assertComparativeEquivalent(expected, actual); // abs(a - b) == 0 or (!(a > b) && !(a < b))
224
+ assertComparativeNotEquivalent(unwanted, actual); // abs(a - b) > 0 or ((a > b) || (a < b))
225
+ assertLess(upperBound, actual); // a < b
226
+ assertMore(lowerBound, actual); // a > b
227
+ assertLessOrEqual(upperBound, actual); // a <= b
228
+ assertMoreOrEqual(lowerBound, actual); // a >= b
229
+ assertTrue(actual);
230
+ assertFalse(actual);
231
+ assertNull(actual);
232
+
233
+ // special cases for floats
234
+ assertEqualFloat(expected, actual, epsilon); // fabs(a - b) <= epsilon
235
+ assertNotEqualFloat(unwanted, actual, epsilon); // fabs(a - b) >= epsilon
236
+ assertInfinity(actual); // isinf(a)
237
+ assertNotInfinity(actual); // !isinf(a)
238
+ assertNAN(arg); // isnan(a)
239
+ assertNotNAN(arg); // !isnan(a)
240
+ ```
241
+
242
+ These functions will report the result of the test to the console, and the testing will continue if they fail.
243
+
244
+ **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.
245
+
246
+
247
+ ### Test Setup and Teardown
248
+
249
+ For steps that are common to all tests, setup and teardown functions may optionally be supplied.
250
+
251
+ ```C++
252
+ #include <ArduinoUnitTests.h>
253
+
254
+ int* myNumber;
255
+
256
+ unittest_setup()
257
+ {
258
+ myNumber = new int(4);
259
+ }
260
+
261
+ unittest_teardown()
262
+ {
263
+ delete myNumber;
264
+ myNumber = NULL;
265
+ }
266
+
267
+ unittest(your_test_name)
268
+ {
269
+ assertEqual(4, *myNumber);
270
+ }
271
+
272
+ unittest_main()
273
+ ```
274
+
275
+
276
+ # Build Scripts
277
+
278
+ For most build environments, the only script that need be executed by the CI system is
279
+
280
+ ```shell
281
+ # simplest build script
282
+ bundle install
283
+ bundle exec arduino_ci.rb
284
+ ```
285
+
286
+ However, more flexible usage is available:
287
+
288
+ ### Custom Versions of external Arduino Libraries
289
+
290
+ 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.
291
+
292
+ 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.
293
+
294
+ ```shell
295
+ # Example build script
296
+ bundle install
297
+
298
+ # ensure the Arduino installation -- creates the Library directory
299
+ bundle exec ensure_arduino_installation.rb
300
+
301
+ # manually install a custom library from a zip file
302
+ wget https://hosting.com/custom_library.zip
303
+ unzip -o custom_library.zip
304
+ mv custom_library $(bundle exec arduino_library_location.rb)
305
+
306
+ # manually install a custom library from a git repository
307
+ git clone https://repository.com/custom_library_repo.git
308
+ mv custom_library_repo $(bundle exec arduino_library_location.rb)
309
+
310
+ # now run CI
311
+ bundle exec arduino_ci.rb
312
+ ```
313
+
314
+ 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.
315
+
316
+
317
+
318
+ # Mocks of Arduino Hardware Functions
319
+
320
+ 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.
321
+
322
+ ## Using `GODMODE`
323
+
324
+ Complete control of the Arduino environment is available in your unit tests through a construct called `GODMODE()`.
325
+
326
+ ```C++
327
+ unittest(example_godmode_stuff)
328
+ {
329
+ GodmodeState* state = GODMODE(); // get access to the state
330
+ state->reset(); // does a full reset of the state.
331
+ state->resetClock(); // - you can reset just the clock (to zero)
332
+ state->resetPins(); // - or just the pins
333
+ state->micros = 1; // manually set the clock such that micros() returns 1
334
+ state->digitalPin[4]; // tells you the commanded state of digital pin 4
335
+ state->digitalPin[4] = HIGH; // digitalRead(4) will now return HIGH
336
+ state->analogPin[3]; // tells you the commanded state of analog pin 3
337
+ state->analogPin[3] = 99; // analogRead(3) will now return 99
338
+ }
339
+ ```
340
+
341
+ ### Pin Histories
342
+
343
+ 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:
344
+
345
+ ```C++
346
+ unittest(pin_history)
347
+ {
348
+ GodmodeState* state = GODMODE();
349
+ int myPin = 3;
350
+ state->reset(); // pin will start LOW
351
+ digitalWrite(myPin, HIGH);
352
+ digitalWrite(myPin, LOW);
353
+ digitalWrite(myPin, LOW);
354
+ digitalWrite(myPin, HIGH);
355
+ digitalWrite(myPin, HIGH);
356
+
357
+ // pin history is queued in case we want to analyze it later.
358
+ // we expect 6 values in that queue (5 that we set plus one
359
+ // initial value), which we'll hard-code here for convenience.
360
+ // (we'll actually assert those 6 values in the next block)
361
+ assertEqual(6, state->digitalPin[1].queueSize());
362
+ bool expected[6] = {LOW, HIGH, LOW, LOW, HIGH, HIGH};
363
+ bool actual[6];
364
+
365
+ // convert history queue into an array so we can verify it.
366
+ // while we're at it, check that we received the amount of
367
+ // elements that we expected.
368
+ int numMoved = state->digitalPin[myPin].toArray(actual, 6);
369
+ assertEqual(6, numMoved);
370
+
371
+ // verify each element
372
+ for (int i = 0; i < 6; ++i) {
373
+ assertEqual(expected[i], actual[i]);
374
+ }
375
+ }
376
+ ```
377
+
378
+
379
+ ### Pin Futures
380
+
381
+ 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.
382
+
383
+ ```C++
384
+ unittest(pin_read_history)
385
+ {
386
+ GodmodeState* state = GODMODE();
387
+ state->reset();
388
+
389
+ int future[6] = {33, 22, 55, 11, 44, 66};
390
+ state->analogPin[1].fromArray(future, 6);
391
+ for (int i = 0; i < 6; ++i)
392
+ {
393
+ assertEqual(future[i], analogRead(1));
394
+ }
395
+
396
+ // for digital pins, we have the added possibility of specifying
397
+ // a stream of input bytes encoded as ASCII
398
+ bool bigEndian = true;
399
+ state->digitalPin[1].fromAscii("Yo", bigEndian);
400
+
401
+ // digitial history as serial data, big-endian
402
+ bool expectedBits[16] = {
403
+ 0, 1, 0, 1, 1, 0, 0, 1, // Y
404
+ 0, 1, 1, 0, 1, 1, 1, 1 // o
405
+ };
406
+
407
+ for (int i = 0; i < 16; ++i) {
408
+ assertEqual(expectedBits[i], digitalRead(1));
409
+ }
410
+ }
411
+ ```
412
+
413
+ ### Serial Data
414
+
415
+ Basic input and output verification of serial port data can be done as follows:
416
+
417
+ ```c++
418
+ unittest(reading_writing_serial)
419
+ {
420
+ GodmodeState* state = GODMODE();
421
+ state->serialPort[0].dataIn = ""; // the queue of data waiting to be read
422
+ state->serialPort[0].dataOut = ""; // the history of data written
423
+
424
+ // When there is no data, nothing happens
425
+ assertEqual(-1, Serial.peek());
426
+ assertEqual("", state->serialPort[0].dataIn);
427
+ assertEqual("", state->serialPort[0].dataOut);
428
+
429
+ // if we put data on the input and peek at it, we see the value and it's not consumed
430
+ state->serialPort[0].dataIn = "a";
431
+ assertEqual('a', Serial.peek());
432
+ assertEqual("a", state->serialPort[0].dataIn);
433
+ assertEqual("", state->serialPort[0].dataOut);
434
+
435
+ // if we read the input, we see the value and it's consumed
436
+ assertEqual('a', Serial.read());
437
+ assertEqual("", state->serialPort[0].dataIn);
438
+ assertEqual("", state->serialPort[0].dataOut);
439
+
440
+ // when we write data, it shows up in the history -- the output buffer
441
+ Serial.write('b');
442
+ assertEqual("", state->serialPort[0].dataIn);
443
+ assertEqual("b", state->serialPort[0].dataOut);
444
+
445
+ // when we print more data, note that the history
446
+ // still contains the first thing we wrote
447
+ Serial.print("cdefg");
448
+ assertEqual("", state->serialPort[0].dataIn);
449
+ assertEqual("bcdefg", state->serialPort[0].dataOut);
450
+ }
451
+ ```
452
+
453
+ A more complicated example: working with serial port IO. Let's say I have the following function:
454
+
455
+ ```C++
456
+ void smartLightswitchSerialHandler(int pin) {
457
+ if (Serial.available() > 0) {
458
+ int incomingByte = Serial.read();
459
+ int val = incomingByte == '0' ? LOW : HIGH;
460
+ Serial.print("Ack ");
461
+ digitalWrite(pin, val);
462
+ Serial.print(String(pin));
463
+ Serial.print(" ");
464
+ Serial.print((char)incomingByte);
465
+ }
466
+ }
467
+ ```
468
+
469
+ 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.
470
+
471
+ ```C++
472
+ unittest(does_nothing_if_no_data)
473
+ {
474
+ // configure initial state
475
+ GodmodeState* state = GODMODE();
476
+ int myPin = 3;
477
+ state->serialPort[0].dataIn = "";
478
+ state->serialPort[0].dataOut = "";
479
+ state->digitalPin[myPin] = LOW;
480
+
481
+ // execute action
482
+ smartLightswitchSerialHandler(myPin);
483
+
484
+ // assess final state
485
+ assertEqual(LOW, state->digitalPin[myPin]);
486
+ assertEqual("", state->serialPort[0].dataIn);
487
+ assertEqual("", state->serialPort[0].dataOut);
488
+ }
489
+
490
+ unittest(two_flips)
491
+ {
492
+ GodmodeState* state = GODMODE();
493
+ int myPin = 3;
494
+ state->serialPort[0].dataIn = "10junk";
495
+ state->serialPort[0].dataOut = "";
496
+ state->digitalPin[myPin] = LOW;
497
+ smartLightswitchSerialHandler(myPin);
498
+ assertEqual(HIGH, state->digitalPin[myPin]);
499
+ assertEqual("0junk", state->serialPort[0].dataIn);
500
+ assertEqual("Ack 3 1", state->serialPort[0].dataOut);
501
+
502
+ state->serialPort[0].dataOut = "";
503
+ smartLightswitchSerialHandler(myPin);
504
+ assertEqual(LOW, state->digitalPin[myPin]);
505
+ assertEqual("junk", state->serialPort[0].dataIn);
506
+ assertEqual("Ack 3 0", state->serialPort[0].dataOut);
507
+ }
508
+ ```
509
+
510
+ ### Pin History as ASCII
511
+
512
+
513
+ 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.
514
+
515
+ ```C++
516
+ int myPin = 3;
517
+
518
+ // digitial history as serial data, big-endian
519
+ bool bigEndian = true;
520
+ bool binaryAscii[24] = {
521
+ 0, 1, 0, 1, 1, 0, 0, 1, // Y
522
+ 0, 1, 1, 0, 0, 1, 0, 1, // e
523
+ 0, 1, 1, 1, 0, 0, 1, 1 // s
524
+ };
525
+
526
+ // "send" these bits
527
+ for (int i = 0; i < 24; digitalWrite(myPin, binaryAscii[i++]));
528
+
529
+ // The first bit in the history is the initial value, which we will ignore
530
+ int offset = 1;
531
+
532
+ // We should be able to parse the bits as ascii
533
+ assertEqual("Yes", state->digitalPin[myPin].toAscii(offset, bigEndian));
534
+ ```
535
+
536
+ 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.
537
+
538
+
539
+ ### Interactivity of "Devices" with Observers
540
+
541
+ 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.
542
+
543
+ 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.
544
+
545
+ ```c++
546
+ class FakeHayesModem : public DeviceUsingBytes {
547
+ public:
548
+ String mLast;
549
+
550
+ FakeHayesModem() : DeviceUsingBytes() {
551
+ mLast = "";
552
+ addResponseLine("AT", "OK");
553
+ addResponseLine("ATV1", "NO CARRIER");
554
+ }
555
+ virtual ~FakeHayesModem() {}
556
+ virtual void onMatchInput(String output) { mLast = output; }
557
+ };
558
+
559
+ unittest(modem_hardware)
560
+ {
561
+ GodmodeState* state = GODMODE();
562
+ state->reset();
563
+ FakeHayesModem m;
564
+ m.attach(&Serial);
565
+
566
+ Serial.write("AT\n");
567
+ assertEqual("AT\n", state->serialPort[0].dataOut);
568
+ assertEqual("OK\n", m.mLast);
569
+ }
570
+ ```
571
+
572
+ 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.
573
+
574
+
575
+ ### Interrupts
576
+
577
+ Although ISRs should be tested directly (as their asynchronous nature is not mocked), the act of attaching or detaching an interrupt can be measured.
578
+
579
+ ```C++
580
+ unittest(interrupt_attachment) {
581
+ GodmodeState *state = GODMODE();
582
+ state->reset();
583
+ assertFalse(state->interrupt[7].attached);
584
+ attachInterrupt(7, (void (*)(void))0, 3);
585
+ assertTrue(state->interrupt[7].attached);
586
+ assertEqual(state->interrupt[7].mode, 3);
587
+ detachInterrupt(7);
588
+ assertFalse(state->interrupt[7].attached);
589
+ }
590
+ ```
591
+
592
+
593
+ ### SPI
594
+
595
+ These basic mocks of SPI store the values in Strings.
596
+
597
+ ```C++
598
+ unittest(spi) {
599
+ GodmodeState *state = GODMODE();
600
+
601
+ // 8-bit
602
+ state->reset();
603
+ state->spi.dataIn = "LMNO";
604
+ uint8_t out8 = SPI.transfer('a');
605
+ assertEqual("a", state->spi.dataOut);
606
+ assertEqual('L', out8);
607
+ assertEqual("MNO", state->spi.dataIn);
608
+
609
+ // 16-bit
610
+ union { uint16_t val; struct { char lsb; char msb; }; } in16, out16;
611
+ state->reset();
612
+ state->spi.dataIn = "LMNO";
613
+ in16.lsb = 'a';
614
+ in16.msb = 'b';
615
+ out16.val = SPI.transfer16(in16.val);
616
+ assertEqual("NO", state->spi.dataIn);
617
+ assertEqual('L', out16.lsb);
618
+ assertEqual('M', out16.msb);
619
+ assertEqual("ab", state->spi.dataOut);
620
+
621
+ // buffer
622
+ state->reset();
623
+ state->spi.dataIn = "LMNOP";
624
+ char inBuf[6] = "abcde";
625
+ SPI.transfer(inBuf, 4);
626
+
627
+ assertEqual("abcd", state->spi.dataOut);
628
+ assertEqual("LMNOe", String(inBuf));
629
+ }
630
+ ```
631
+
632
+ ### EEPROM
633
+
634
+ `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.)
635
+
636
+ ```C++
637
+ unittest(eeprom)
638
+ {
639
+ uint8_t a;
640
+ // size
641
+ assertEqual(EEPROM_SIZE, EEPROM.length());
642
+ // initial values
643
+ a = EEPROM.read(0);
644
+ assertEqual(255, a);
645
+ // write and read
646
+ EEPROM.write(0, 24);
647
+ a = EEPROM.read(0);
648
+ assertEqual(24, a);
649
+ // update
650
+ EEPROM.write(1, 14);
651
+ EEPROM.update(1, 22);
652
+ a = EEPROM.read(1);
653
+ assertEqual(22, a);
654
+ // put and get
655
+ const float f1 = 0.025f;
656
+ float f2 = 0.0f;
657
+ EEPROM.put(5, f1);
658
+ assertEqual(0.0f, f2);
659
+ EEPROM.get(5, f2);
660
+ assertEqual(0.025f, f2);
661
+ // array access
662
+ int val = 10;
663
+ EEPROM[2] = val;
664
+ a = EEPROM[2];
665
+ assertEqual(10, a);
666
+ }
667
+ ```
668
+
669
+
670
+ ### Wire
671
+
672
+ This library allows communication with I2C / TWI devices.
673
+
674
+ The interface the library has been fully mocked, with the addition of several functions for debugging
675
+
676
+ * `Wire.resetMocks()`: Initializes all mocks, and for test repeatability should be called at the top of any unit tests that use Wire.
677
+ * `Wire.didBegin()`: returns whether `Wire.begin()` was called at any point
678
+ * `Wire.getMosi(address)`: returns a pointer to a `deque` that represents the history of data sent to `address`
679
+ * `Wire.getMiso(address)`: returns a pointer to a `deque` that defines what the master will read from `address` (i.e. for you to supply)
680
+
681
+ ```c++
682
+ unittest(wire_basics) {
683
+ // ensure known starting state
684
+ Wire.resetMocks();
685
+
686
+ // in case you need to check that your library is properly calling .begin()
687
+ assertFalse(Wire.didBegin());
688
+ Wire.begin();
689
+ assertTrue(Wire.didBegin());
690
+
691
+ // pick a random device. master write buffer should be empty
692
+ const uint8_t randomSlaveAddr = 14;
693
+ deque<uint8_t>* mosi = Wire.getMosi(randomSlaveAddr);
694
+ assertEqual(0, mosi->size());
695
+
696
+ // write some random data to random device
697
+ const uint8_t randomData[] = { 0x07, 0x0E };
698
+ Wire.beginTransmission(randomSlaveAddr);
699
+ Wire.write(randomData[0]);
700
+ Wire.write(randomData[1]);
701
+ Wire.endTransmission();
702
+
703
+ // check master write buffer values
704
+ assertEqual(2, mosi->size());
705
+ assertEqual(randomData[0], mosi->front());
706
+ mosi->pop_front();
707
+ assertEqual(randomData[1], mosi->front());
708
+ mosi->pop_front();
709
+ assertEqual(0, mosi->size());
710
+ }
711
+ ```