Skip to content

More CI fixes #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jan 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,23 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Added
- C++ functions for `assure`; `assert`s will run tests and continue, `assure`s will abort on failures
- Missing dotfiles in the `DoSomething` project have been committed

### Changed
- `arduino_ci_remote.rb` doesn't attempt to set URLs if nothing needs to be downloaded
- `arduino_ci_remote.rb` does unit tests first
- `unittest_main()` is now the macro for the `int main()` of test files

### Deprecated

### Removed

### Fixed
- All test files were reporting "not ok" in TAP output. Now they are OK iff all asserts pass.
- Directories with a C++ extension in their name could cause problems. Now they are ignored.
- `CppLibrary` had trouble with symlinks. It shoudn't anymore.
- `CppLibrary` had trouble with vendor bundles. It might in the future, but I have a better fix ready to go if it's an issue.

### Security

Expand Down
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,10 @@ unittest(your_test_name)
assertEqual(4, doSomething());
}

int main(int argc, char *argv[]) {
return Test::run_and_report(argc, argv);
}
unittest_main()
```

This test defines one `unittest` (a macro provided by `ArduionUnitTests.h`), called `your_test_name`, which makes some assertions on the target library. The `int main` section is boilerplate.
This test defines one `unittest` (a macro provided by `ArduionUnitTests.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.


## More Documentation
Expand Down
13 changes: 13 additions & 0 deletions SampleProjects/DoSomething/.arduino-ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
compile:
libraries: ~
platforms:
- uno
- due
- leonardo

unittest:
libraries: ~
platforms:
- uno
- due
- leonardo
5 changes: 5 additions & 0 deletions SampleProjects/DoSomething/.travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
sudo: false
language: ruby
script:
- bundle install
- bundle exec arduino_ci_remote.rb
4 changes: 1 addition & 3 deletions SampleProjects/DoSomething/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,7 @@ unittest(your_test_name)
assertEqual(4, doSomething());
}

int main(int argc, char *argv[]) {
return Test::run_and_report(argc, argv);
}
unittest_main()
```

This test defines one `unittest` (a macro provided by `ArduionUnitTests.h`), called `your_test_name`, which makes some assertions on the target library. The `int main` section is boilerplate.
Expand Down
4 changes: 1 addition & 3 deletions SampleProjects/DoSomething/test/good-library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,4 @@ unittest(library_does_something)
assertEqual(4, doSomething());
}

int main(int argc, char *argv[]) {
return Test::run_and_report(argc, argv);
}
unittest_main()
4 changes: 1 addition & 3 deletions SampleProjects/DoSomething/test/good-null.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,4 @@ unittest(nothing)
{
}

int main(int argc, char *argv[]) {
return Test::run_and_report(argc, argv);
}
unittest_main()
4 changes: 1 addition & 3 deletions SampleProjects/TestSomething/test/bad-null.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,4 @@ unittest(pretend_equal_things_arent)
assertNotEqual(x, y);
}

int main(int argc, char *argv[]) {
return Test::run_and_report(argc, argv);
}
unittest_main()
4 changes: 1 addition & 3 deletions SampleProjects/TestSomething/test/good-library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,4 @@ unittest(library_tests_something)
assertEqual(4, testSomething());
}

int main(int argc, char *argv[]) {
return Test::run_and_report(argc, argv);
}
unittest_main()
4 changes: 1 addition & 3 deletions SampleProjects/TestSomething/test/good-null.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,4 @@ unittest(nothing)
{
}

int main(int argc, char *argv[]) {
return Test::run_and_report(argc, argv);
}
unittest_main()
20 changes: 16 additions & 4 deletions cpp/unittest/ArduinoUnitTests.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,16 @@ class Test
Results results = {0, 0, 0};

for (Test *p = sRoot; p; p = p->mNext) {
TestData td = {p->name(), p->result()};
p->prepare();
p->mReporter = reporter;
if (reporter) reporter->onTestStart(td);
TestData t1 = {p->name(), p->result()};
if (reporter) reporter->onTestStart(t1);
p->test();
if (p->mResult == RESULT_PASS) ++results.passed;
if (p->mResult == RESULT_FAIL) ++results.failed;
if (p->mResult == RESULT_SKIP) ++results.skipped;
if (reporter) reporter->onTestEnd(td);
TestData t2 = {p->name(), p->result()};
if (reporter) reporter->onTestEnd(t2);
}

return results;
Expand All @@ -154,8 +156,12 @@ class Test
return results.failed + results.skipped;
}

void prepare() {
mResult = RESULT_PASS; // not None, and not fail unless we hear otherwise
}

void test() {
mResult = RESULT_PASS; // not None, and not fail unless we hear otherwise
// thin wrapper. nothing to do here for now
task();
}

Expand Down Expand Up @@ -210,3 +216,9 @@ class Test
void task(); \
} test_##name##_instance; \
void test_##name ::task()


#define unittest_main() \
int main(int argc, char *argv[]) { \
return Test::run_and_report(argc, argv); \
}
45 changes: 32 additions & 13 deletions cpp/unittest/Assertion.h
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
#pragma once
#include "Compare.h"

// helper define for the operators below
#define assertOp(desc, relevance1, arg1, op, op_name, relevance2, arg2) \
do \
{ \
if (!assertion<typeof(arg1), typeof(arg2)>(__FILE__, __LINE__, \
desc, \
relevance1, #arg1, (arg1), \
op_name, op, \
relevance2, #arg2, (arg2))) \
{ \
return; \
} \
#define testBehaviorOp(die, desc, rel1, arg1, op, op_name, rel2, arg2) \
do \
{ \
if (!assertion<typeof(arg1), typeof(arg2)>(__FILE__, __LINE__, \
desc, \
rel1, #arg1, (arg1), \
op_name, op, \
rel2, #arg2, (arg2))) \
{ \
if (die) return; \
} \
} while (0)

/** macro generates optional output and calls fail() followed by a return if false. */


// helper define for the operators below
#define assertOp(desc, rel1, arg1, op, op_name, rel2, arg2) \
testBehaviorOp(false, desc, rel1, arg1, op, op_name, rel2, arg2)

#define assureOp(desc, rel1, arg1, op, op_name, rel2, arg2) \
testBehaviorOp(true, desc, rel1, arg1, op, op_name, rel2, arg2)


/** macro generates optional output and calls fail() but does not return if false. */
#define assertEqual(arg1,arg2) assertOp("assertEqual","expected",arg1,compareEqual,"==","actual",arg2)
#define assertNotEqual(arg1,arg2) assertOp("assertNotEqual","unwanted",arg1,compareNotEqual,"!=","actual",arg2)
#define assertLess(arg1,arg2) assertOp("assertLess","lowerBound",arg1,compareLess,"<","upperBound",arg2)
Expand All @@ -25,3 +34,13 @@
#define assertTrue(arg) assertEqual(arg,true)
#define assertFalse(arg) assertEqual(arg,false)

/** macro generates optional output and calls fail() followed by a return if false. */
#define assureEqual(arg1,arg2) assureOp("assureEqual","expected",arg1,compareEqual,"==","actual",arg2)
#define assureNotEqual(arg1,arg2) assureOp("assureNotEqual","unwanted",arg1,compareNotEqual,"!=","actual",arg2)
#define assureLess(arg1,arg2) assureOp("assureLess","lowerBound",arg1,compareLess,"<","upperBound",arg2)
#define assureMore(arg1,arg2) assureOp("assureMore","upperBound",arg1,compareMore,">","lowerBound",arg2)
#define assureLessOrEqual(arg1,arg2) assureOp("assureLessOrEqual","lowerBound",arg1,compareLessOrEqual,"<=","upperBound",arg2)
#define assureMoreOrEqual(arg1,arg2) assureOp("assureMoreOrEqual","upperBound",arg1,compareMoreOrEqual,">=","lowerBound",arg2)
#define assureTrue(arg) assertEqual(arg,true)
#define assureFalse(arg) assertEqual(arg,false)

46 changes: 24 additions & 22 deletions exe/arduino_ci_remote.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ def assure(message, &block)
# do that, set the URLs, and download the packages
all_packages = all_platforms.values.map { |v| v[:package] }.uniq.reject(&:nil?)
all_urls = all_packages.map { |p| config.package_url(p) }.uniq.reject(&:nil?)
assure("Setting board manager URLs") do
@arduino_cmd.set_pref("boardsmanager.additional.urls", all_urls.join(","))
unless all_urls.empty?
assure("Setting board manager URLs") do
@arduino_cmd.set_pref("boardsmanager.additional.urls", all_urls.join(","))
end
end

all_packages.each do |p|
Expand All @@ -86,26 +88,6 @@ def assure(message, &block)
assure("Installing aux library '#{l}'") { @arduino_cmd.install_library(l) }
end

attempt("Setting compiler warning level") { @arduino_cmd.set_pref("compiler.warning_level", "all") }

library_examples.each do |example_path|
ovr_config = config.from_example(example_path)
ovr_config.platforms_to_build.each do |p|
board = all_platforms[p][:board]
assure("Switching to board for #{p} (#{board})") { @arduino_cmd.use_board(board) }
example_name = File.basename(example_path)
attempt("Verifying #{example_name}") do
ret = @arduino_cmd.verify_sketch(example_path)
unless ret
puts
puts "Last command: #{@arduino_cmd.last_msg}"
puts @arduino_cmd.last_err
end
ret
end
end
end

config.platforms_to_unittest.each do |p|
board = all_platforms[p][:board]
assure("Switching to board for #{p} (#{board})") { @arduino_cmd.use_board(board) }
Expand All @@ -129,4 +111,24 @@ def assure(message, &block)
end
end

attempt("Setting compiler warning level") { @arduino_cmd.set_pref("compiler.warning_level", "all") }

library_examples.each do |example_path|
ovr_config = config.from_example(example_path)
ovr_config.platforms_to_build.each do |p|
board = all_platforms[p][:board]
assure("Switching to board for #{p} (#{board})") { @arduino_cmd.use_board(board) }
example_name = File.basename(example_path)
attempt("Verifying #{example_name}") do
ret = @arduino_cmd.verify_sketch(example_path)
unless ret
puts
puts "Last command: #{@arduino_cmd.last_msg}"
puts @arduino_cmd.last_err
end
ret
end
end
end

terminate(true)
27 changes: 24 additions & 3 deletions lib/arduino_ci/cpp_library.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,28 @@ def initialize(base_dir)
@last_msg = ""
end

# Guess whether a file is part of the vendor bundle (indicating we should ignore it).
#
# This assumes the vendor bundle will be at `vendor/bundle` and not some other location
# @param path [String] The path to check
# @return [Array<String>] The paths of the found files
def vendor_bundle?(path)
# TODO: look for Gemfile, look for .bundle/config and get BUNDLE_PATH from there?
base = File.join(@base_dir, "vendor")
real = File.join(File.realpath(@base_dir), "vendor")
return true if path.start_with?(base)
return true if path.start_with?(real)
false
end

# Get a list of all CPP source files in a directory and its subdirectories
# @param some_dir [String] The directory in which to begin the search
# @return [Array<String>] The paths of the found files
def cpp_files_in(some_dir)
real = File.realpath(some_dir)
Find.find(real).select { |path| CPP_EXTENSIONS.include?(File.extname(path)) }
files = Find.find(real).reject { |path| File.directory?(path) }
ret = files.select { |path| CPP_EXTENSIONS.include?(File.extname(path)) }
ret
end

# CPP files that are part of the project library under test
Expand All @@ -50,6 +66,7 @@ def cpp_files
cpp_files_in(@base_dir).reject do |p|
next true if File.dirname(p).include?(tests_dir)
next true if File.dirname(p).include?(real_tests_dir)
next true if vendor_bundle?(p)
end
end

Expand Down Expand Up @@ -80,8 +97,12 @@ def test_files
# Find all directories in the project library that include C++ header files
# @return [Array<String>]
def header_dirs
files = Find.find(@base_dir).select { |path| HPP_EXTENSIONS.include?(File.extname(path)) }
files.map { |path| File.dirname(path) }.uniq
real = File.realpath(@base_dir)
all_files = Find.find(real).reject { |path| File.directory?(path) }
unbundled = all_files.reject { |path| vendor_bundle?(path) }
files = unbundled.select { |path| HPP_EXTENSIONS.include?(File.extname(path)) }
ret = files.map { |path| File.dirname(path) }.uniq
ret
end

# wrapper for the GCC command
Expand Down
8 changes: 6 additions & 2 deletions misc/default.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Note that ci_config_spec.rb has tests for this file's contents

packages:
esp8266:esp8266:
url: http://arduino.esp8266.com/stable/package_esp8266com_index.json
Expand All @@ -22,15 +24,15 @@ platforms:
warnings:
flags:
zero:
board: arduino:samd:zero
board: arduino:samd:arduino_zero_native
package: arduino:samd
gcc:
features:
defines:
warnings:
flags:
esp8266:
board: esp8266:esp8266:huzzah
board: esp8266:esp8266:huzzah:FlashSize=4M3M,CpuFrequency=80
package: esp8266:esp8266
gcc:
features:
Expand Down Expand Up @@ -67,11 +69,13 @@ compile:
platforms:
- uno
- due
- zero
- leonardo

unittest:
libraries: ~
platforms:
- uno
- due
- zero
- leonardo
6 changes: 3 additions & 3 deletions spec/ci_config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@

zero = default_config.platform_definition("zero")
expect(zero.class).to eq(Hash)
expect(zero[:board]).to eq("arduino:samd:zero")
expect(zero[:board]).to eq("arduino:samd:arduino_zero_native")
expect(zero[:package]).to eq("arduino:samd")
expect(zero[:gcc].class).to eq(Hash)

expect(default_config.package_url("adafruit:avr")).to eq("https://adafruit.github.io/arduino-board-index/package_adafruit_index.json")
expect(default_config.platforms_to_build).to match(["uno", "due", "leonardo"])
expect(default_config.platforms_to_unittest).to match(["uno", "due", "leonardo"])
expect(default_config.platforms_to_build).to match(["uno", "due", "zero", "leonardo"])
expect(default_config.platforms_to_unittest).to match(["uno", "due", "zero", "leonardo"])
expect(default_config.aux_libraries_for_build).to match([])
expect(default_config.aux_libraries_for_unittest).to match([])
end
Expand Down