Skip to content

Build a shared library for use with each test #294

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 59 commits into from
Oct 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
4667db9
Speed up tests by building a shared library with everything but the t…
jgfoster Mar 24, 2021
2284f1d
Add meaningful comments and remove commented-out code.
jgfoster Mar 24, 2021
452cf08
See if this fixes the lint complaints.
jgfoster Mar 24, 2021
24c37b6
Build single test executable if we don't have a shared library.
jgfoster Mar 24, 2021
432828f
Use `File.exist?()` instead of `File.exists?()`.
jgfoster Mar 24, 2021
009f732
Set LD_LIBRARY_PATH to directory with shared library.
jgfoster Mar 24, 2021
e24ec9e
Fix formatting (lint) issue.
jgfoster Mar 24, 2021
2f5153f
Should find LD_LIBRARY_PATH on Linux.
jgfoster Mar 24, 2021
a9bc8b9
Merge branch 'master' into shared_library
Oct 3, 2021
81720df
Fix merge problem with hiding build artifacts.
jgfoster Oct 3, 2021
cc95b8d
Merge branch 'master' into shared_library
Oct 3, 2021
263f7da
Merge branch 'master' into shared_library
jgfoster Oct 4, 2021
df50e04
Merge branch 'master' into shared_library
jgfoster Oct 10, 2021
ecdd933
Use constant variables for names and fix problem where library was no…
jgfoster Oct 10, 2021
9a9412d
Separate rubocop from general testing in LInux.
jgfoster Oct 10, 2021
932253e
Replace `.sh` with `.so` for shared library name.
jgfoster Oct 10, 2021
76bf627
Debugging output to look for missing library.
jgfoster Oct 10, 2021
d688e3c
Fix output of arg_sets.
jgfoster Oct 10, 2021
481d911
See if we can find the files!
jgfoster Oct 10, 2021
904ffc4
More attempts at directory listing.
jgfoster Oct 10, 2021
c290df8
Forgot to print output.
jgfoster Oct 10, 2021
75f2de0
See if we can pass on macOS.
jgfoster Oct 10, 2021
cfebe1f
Fix `LD_LIBRARY_PATH`
jgfoster Oct 10, 2021
49c2439
Remove directory logging; add lines between rubocop and rspec code.
jgfoster Oct 10, 2021
01ed91b
Rename GitHub Action workflow job.
jgfoster Oct 10, 2021
acc3f01
Rename "lint" to "rubocop_and_rspec" in test.
jgfoster Oct 10, 2021
224b4d2
Switch windows test back to ubuntu to avoid test failure.
jgfoster Oct 10, 2021
6fe2bc0
Try with libarduino.so in working directory instead of build directory.
jgfoster Oct 10, 2021
bc0270f
Revert "Try with libarduino.so in working directory instead of build …
jgfoster Oct 10, 2021
e994596
Merge branch 'master' into shared_library
Oct 15, 2021
c5246fc
Does Windows find library if it is in the local directory?
jgfoster Oct 15, 2021
c4bfe14
Revert "Does Windows find library if it is in the local directory?"
jgfoster Oct 15, 2021
81311ab
Does adding to PATH help Windows find the library?
jgfoster Oct 15, 2021
427c6ea
Add debugging info for Windows.
jgfoster Oct 15, 2021
7170282
The path seems to be structured differently in some tests.
jgfoster Oct 15, 2021
83b93b7
Try a different path for Windows.
jgfoster Oct 15, 2021
99a6d7e
One more try tonight for a Windows path to the shared library.
jgfoster Oct 15, 2021
e751715
Try with .dll as the library name.
jgfoster Oct 15, 2021
5784d94
Address a couple lint issues and see if the path slashes are important.
jgfoster Oct 15, 2021
e51501d
More lint issues.
jgfoster Oct 15, 2021
910c4ce
Refactor to clean up Windows vs. non-Windows code.
jgfoster Oct 15, 2021
9c4c487
Does this fix the lint error?
jgfoster Oct 15, 2021
abbc7a5
Refactor the expression to try to foil lint complaint.
jgfoster Oct 15, 2021
e0da3f7
Create standalone function to build the shared library.
jgfoster Oct 17, 2021
bd4b916
Address lint issues.
jgfoster Oct 17, 2021
b38752d
Another lint issue.
jgfoster Oct 17, 2021
0314005
More rubocopy and rspec cleanup.
jgfoster Oct 17, 2021
059e349
rename function and give the right number of parameters
jgfoster Oct 17, 2021
a16e33e
Split rubocop and rspec. Fix typo in variable name.
jgfoster Oct 17, 2021
c20d61b
File missed in last commit.
jgfoster Oct 17, 2021
68d8f03
Missed variable name replacement.
jgfoster Oct 17, 2021
3d7926f
See if we can avoid Rubocop error in Windows.
jgfoster Oct 17, 2021
bf6bb03
Still trying on Windows!
jgfoster Oct 17, 2021
c086fc8
Treat lowercase `itoa()` as successful.
jgfoster Oct 17, 2021
caa990a
Undo last edit.
jgfoster Oct 17, 2021
1ccae14
Upshift `itoa()` result so that Windows result matches expected values.
jgfoster Oct 17, 2021
0cbb275
Fix last commit.
jgfoster Oct 17, 2021
e7c1098
Build shared library only once for `cpp_library_spec.rb`.
jgfoster Oct 17, 2021
6a5cd78
Revert "Build shared library only once for `cpp_library_spec.rb`."
jgfoster Oct 17, 2021
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
26 changes: 25 additions & 1 deletion .github/workflows/linux.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name: linux
on: [push, pull_request]

jobs:
"unittest_lint_sampleproject":
"rubocop":
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
Expand All @@ -17,7 +17,31 @@ jobs:
bundle install
bundle exec rubocop --version
bundle exec rubocop -D .

"rspec":
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6
- name: Check style, functionality, and usage
run: |
g++ -v
bundle install
bundle exec rspec --backtrace

"TestSomething":
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6
- name: TestSomething
run: |
g++ -v
bundle install
cd SampleProjects/TestSomething
bundle install
bundle exec arduino_ci.rb
Expand Down
26 changes: 25 additions & 1 deletion .github/workflows/macos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name: macos
on: [push, pull_request]

jobs:
"unittest_lint_sampleproject":
"rubocop":
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
Expand All @@ -17,7 +17,31 @@ jobs:
bundle install
bundle exec rubocop --version
bundle exec rubocop -D .

"rspec":
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6
- name: Check style, functionality, and usage
run: |
g++ -v
bundle install
bundle exec rspec --backtrace

"TestSomething":
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6
- name: TestSomething
run: |
g++ -v
bundle install
cd SampleProjects/TestSomething
bundle install
bundle exec arduino_ci.rb
Expand Down
15 changes: 14 additions & 1 deletion .github/workflows/windows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name: windows
on: [push, pull_request]

jobs:
"unittest_lint_sampleproject":
"rubocop_and_rspec":
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
Expand All @@ -17,7 +17,20 @@ jobs:
bundle install
bundle exec rubocop --version
bundle exec rubocop -D .
echo "done with Rubocop (See https://github.com/Arduino-CI/arduino_ci/issues/315)"
bundle exec rspec --backtrace

"TestSomething":
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6
- name: TestSomething
run: |
g++ -v
bundle install
cd SampleProjects/TestSomething
bundle install
bundle exec arduino_ci.rb
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ Gemfile.lock
/spec/reports/
vendor
*.gem
.arduino_ci

# rspec failure tracking
.rspec_status

# C++ stuff
*.bin
*.bin.dSYM
*.so
*.so.dSYM
.vscode
9 changes: 5 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Support for `dtostrf()`

### Changed
- Fix copy/paste error to allow additional warnings for a platform
- Properly report compile errors in GitHub Actions (#296)
- We now compile a shared library to be used for each test.
- Put build artifacts in a separate directory to reduce clutter.
- Replace `#define yield() _NOP()` with `inline void yield() { _NOP(); }` so that other code can define a `yield()` function.
- Update .gitattributes so we have consistent line endings
- Change 266 files from CRLF to LF.
- Run tests on push as well as on a pull request so developers can see impact
- Apply "rule of three" to Client copy constructor and copy assignment operator

### Deprecated

### Removed

### Fixed
- Properly report compile errors in GitHub Actions.
- Fix copy/paste error to allow additional warnings for a platform
- Apply "rule of three" to Client copy constructor and copy assignment operator

### Security

Expand Down Expand Up @@ -397,7 +398,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Fixed
- Malformed YAML (duplicate unittests section) now has no duplicate section
- arduino_ci_remote.rb script now has correct arguments in build_for_test_with_configuration
- arduino_ci_remote.rb script now has correct arguments in build_for_test


## [0.1.8] - 2018-04-03
Expand Down
24 changes: 14 additions & 10 deletions SampleProjects/TestSomething/test/stdlib.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#include <ArduinoUnitTests.h>
#include <Arduino.h>
#include <ctype.h>
#include <string.h>

#define ARRAY_SIZEOF(a) ( sizeof(a) / sizeof((a)[0]) )

unittest(library_tests_itoa)
{
char buf[32];
const char *result;
struct {
int value;
const char *expected;
Expand All @@ -26,19 +27,22 @@ unittest(library_tests_itoa)
};

for (int i = 0; i < ARRAY_SIZEOF(table); i++) {
result = itoa(table[i].value, buf, table[i].base);
assertEqual(table[i].expected, result);
itoa(table[i].value, buf, table[i].base);
for (int j = 0; j < strlen(buf); ++j) {
buf[j] = toupper(buf[j]);
}
assertEqual(table[i].expected, buf);
}

// While only bases 2, 8, 10 and 16 are of real interest, lets test that all
// While only bases 2, 8, 10 and 16 are of real interest, let's test that all
// bases at least produce expected output for a few test points simple to test.
for (int base = 2; base <= 16; base++) {
result = itoa(0, buf, base);
assertEqual("0", result);
result = itoa(1, buf, base);
assertEqual("1", result);
result = itoa(base, buf, base);
assertEqual("10", result);
itoa(0, buf, base);
assertEqual("0", buf);
itoa(1, buf, base);
assertEqual("1", buf);
itoa(base, buf, base);
assertEqual("10", buf);
}

}
Expand Down
36 changes: 27 additions & 9 deletions exe/arduino_ci.rb
Original file line number Diff line number Diff line change
Expand Up @@ -421,16 +421,16 @@ def perform_unit_tests(cpp_library, file_config)

platforms.each do |p|
puts
config.allowable_unittest_files(cpp_library.test_files).each do |unittest_path|
unittest_name = unittest_path.basename.to_s
compilers.each do |gcc_binary|
compilers.each do |gcc_binary|
# before compiling the tests, build a shared library of everything except the test code
next unless build_shared_library(gcc_binary, p, config, cpp_library)

# now build and run each test using the shared library build above
config.allowable_unittest_files(cpp_library.test_files).each do |unittest_path|
unittest_name = unittest_path.basename.to_s
puts "--------------------------------------------------------------------------------"
attempt_multiline("Unit testing #{unittest_name} with #{gcc_binary} for #{p}") do
exe = cpp_library.build_for_test_with_configuration(
unittest_path,
config.aux_libraries_for_unittest,
gcc_binary,
config.gcc_config(p)
)
exe = cpp_library.build_for_test(unittest_path, gcc_binary)
puts
unless exe
puts "Last command: #{cpp_library.last_cmd}"
Expand All @@ -445,6 +445,24 @@ def perform_unit_tests(cpp_library, file_config)
end
end

def build_shared_library(gcc_binary, platform, config, cpp_library)
attempt_multiline("Build shared library with #{gcc_binary} for #{platform}") do
exe = cpp_library.build_shared_library(
config.aux_libraries_for_unittest,
gcc_binary,
config.gcc_config(platform)
)
puts
unless exe
puts "Last command: #{cpp_library.last_cmd}"
puts cpp_library.last_out
puts cpp_library.last_err
return false
end
return true
end
end

def perform_example_compilation_tests(cpp_library, config)
phase("Compilation of example sketches")
if @cli_options[:skip_compilation]
Expand Down
73 changes: 57 additions & 16 deletions lib/arduino_ci/cpp_library.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
require "arduino_ci/host"
require 'pathname'
require 'shellwords'
require 'os'

HPP_EXTENSIONS = [".hpp", ".hh", ".h", ".hxx", ".h++"].freeze
CPP_EXTENSIONS = [".cpp", ".cc", ".c", ".cxx", ".c++"].freeze
CI_CPP_DIR = Pathname.new(__dir__).parent.parent + "cpp"
ARDUINO_HEADER_DIR = CI_CPP_DIR + "arduino"
UNITTEST_HEADER_DIR = CI_CPP_DIR + "unittest"
LIBRARY_NAME = "arduino".freeze
BUILD_DIR = "#{Dir.pwd}/.arduino_ci".freeze # hide build artifacts

module ArduinoCI

Expand Down Expand Up @@ -464,16 +467,15 @@ def flag_args(ci_gcc_config)
ci_gcc_config[:flags]
end

# All GCC command line args for building any unit test
# All non-CPP GCC command line args for building any unit test.
# We leave out the CPP files so they can be included or not
# depending on whether we are building a shared library.
# @param aux_libraries [Array<Pathname>] The external Arduino libraries required by this project
# @param ci_gcc_config [Hash] The GCC config object
# @return [Array<String>] GCC command-line flags
def test_args(aux_libraries, ci_gcc_config)
# TODO: something with libraries?
ret = include_args(aux_libraries)
ret += cpp_files_arduino.map(&:to_s)
ret += cpp_files_unittest.map(&:to_s)
ret += cpp_files.map(&:to_s)
unless ci_gcc_config.nil?
cgc = ci_gcc_config
ret = feature_args(cgc) + warning_args(cgc) + define_args(cgc) + flag_args(cgc) + ret
Expand All @@ -486,18 +488,52 @@ def test_args(aux_libraries, ci_gcc_config)
# The dependent libraries configuration is appended with data from library.properties internal to the library under test
#
# @param test_file [Pathname] The path to the file containing the unit tests
# @param gcc_binary [String] name of a compiler
# @return [Pathname] path to the compiled test executable
def build_for_test(test_file, gcc_binary)
executable = Pathname.new("#{BUILD_DIR}/#{test_file.basename}.bin").expand_path
File.delete(executable) if File.exist?(executable)
arg_sets = ["-std=c++0x", "-o", executable.to_s, "-L#{BUILD_DIR}", "-DARDUINO=100"]
if libasan?(gcc_binary)
arg_sets << [ # Stuff to help with dynamic memory mishandling
"-g", "-O1",
"-fno-omit-frame-pointer",
"-fno-optimize-sibling-calls",
"-fsanitize=address"
]
end
arg_sets << @test_args
arg_sets << [test_file.to_s, "-l#{LIBRARY_NAME}"]
args = arg_sets.flatten(1)
return nil unless run_gcc(gcc_binary, *args)

artifacts << executable
executable
end

# build a shared library to be used by each test
#
# The dependent libraries configuration is appended with data from library.properties internal to the library under test
#
# @param aux_libraries [Array<Pathname>] The external Arduino libraries required by this project
# @param gcc_binary [String] name of a compiler
# @param ci_gcc_config [Hash] The GCC config object
# @return [Pathname] path to the compiled test executable
def build_for_test_with_configuration(test_file, aux_libraries, gcc_binary, ci_gcc_config)
base = test_file.basename
# hide build artifacts
build_dir = '.arduino_ci'
Dir.mkdir build_dir unless File.exist?(build_dir)
executable = Pathname.new("#{build_dir}/unittest_#{base}.bin").expand_path
def build_shared_library(aux_libraries, gcc_binary, ci_gcc_config)
Dir.mkdir BUILD_DIR unless File.exist?(BUILD_DIR)
if OS.windows?
flag = ENV["PATH"].include? ";"
ENV["PATH"] = BUILD_DIR + (flag ? ";" : ":") + ENV["PATH"] unless ENV["PATH"].include? BUILD_DIR
suffix = "dll"
else
ENV["LD_LIBRARY_PATH"] = BUILD_DIR
suffix = "so"
end
full_lib_name = "#{BUILD_DIR}/lib#{LIBRARY_NAME}.#{suffix}"
executable = Pathname.new(full_lib_name).expand_path
File.delete(executable) if File.exist?(executable)
arg_sets = []
arg_sets << ["-std=c++0x", "-o", executable.to_s, "-DARDUINO=100"]
arg_sets = ["-std=c++0x", "-shared", "-fPIC", "-Wl,-undefined,dynamic_lookup",
"-o", executable.to_s, "-L#{BUILD_DIR}", "-DARDUINO=100"]
if libasan?(gcc_binary)
arg_sets << [ # Stuff to help with dynamic memory mishandling
"-g", "-O1",
Expand All @@ -509,10 +545,15 @@ def build_for_test_with_configuration(test_file, aux_libraries, gcc_binary, ci_g

# combine library.properties defs (if existing) with config file.
# TODO: as much as I'd like to rely only on the properties file(s), I think that would prevent testing 1.0-spec libs
full_dependencies = all_arduino_library_dependencies!(aux_libraries)
arg_sets << test_args(full_dependencies, ci_gcc_config)
arg_sets << cpp_files_libraries(full_dependencies).map(&:to_s)
arg_sets << [test_file.to_s]
# the following two take some time, so are cached when we build the shared library
@full_dependencies = all_arduino_library_dependencies!(aux_libraries)
@test_args = test_args(@full_dependencies, ci_gcc_config) # build full set of include directories to be cached for later

arg_sets << @test_args
arg_sets << cpp_files_arduino.map(&:to_s) # Arduino.cpp, Godmode.cpp, and stdlib.cpp
arg_sets << cpp_files_unittest.map(&:to_s) # ArduinoUnitTests.cpp
arg_sets << cpp_files.map(&:to_s) # CPP files for the primary application library under test
arg_sets << cpp_files_libraries(@full_dependencies).map(&:to_s) # CPP files for all the libraries we depend on
args = arg_sets.flatten(1)
return nil unless run_gcc(gcc_binary, *args)

Expand Down
Loading