From 8ed05e0c759ed83405c57acc948b7f0f047b6693 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Wed, 10 Jan 2018 08:39:45 -0500 Subject: [PATCH 1/3] initial gem setup --- .gitignore | 12 ++++ .rspec | 2 + .rubocop.yml | 80 +++++++++++++++++++++++++ .travis.yml | 14 +++++ .yardopts | 1 + CHANGELOG.md | 27 +++++++++ CONTRIBUTING.md | 29 +++++++++ Gemfile | 6 ++ README.md | 41 ++++++++++++- arduino_ci.gemspec | 27 +++++++++ bin/console | 14 +++++ bin/setup | 8 +++ lib/arduino_ci.rb | 123 ++++++++++++++++++++++++++++++++++++++ lib/arduino_ci/version.rb | 3 + spec/arduino_ci_spec.rb | 7 +++ spec/arduino_exec_spec.rb | 30 ++++++++++ spec/spec_helper.rb | 14 +++++ 17 files changed, 436 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 .rspec create mode 100644 .rubocop.yml create mode 100644 .travis.yml create mode 100644 .yardopts create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 Gemfile create mode 100644 arduino_ci.gemspec create mode 100755 bin/console create mode 100755 bin/setup create mode 100644 lib/arduino_ci.rb create mode 100644 lib/arduino_ci/version.rb create mode 100644 spec/arduino_ci_spec.rb create mode 100644 spec/arduino_exec_spec.rb create mode 100644 spec/spec_helper.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..be26b81f --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +/.bundle/ +/.yardoc +/Gemfile.lock +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +vendor + +# rspec failure tracking +.rspec_status diff --git a/.rspec b/.rspec new file mode 100644 index 00000000..8c18f1ab --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--format documentation +--color diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 00000000..f89ea5b5 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,80 @@ +AllCops: + Exclude: + - '*.gemspec' + - 'spec/*.rb' + - '**/generated_parser/*' + - './vendor/**/*' + +# inherit_from: .rubocop_todo.yml + +# Extra lines for readability +Layout/EmptyLinesAroundClassBody: + Enabled: false + +Layout/EmptyLinesAroundMethodBody: + Enabled: false + +Layout/EmptyLinesAroundModuleBody: + Enabled: false + +# Configuration parameters: AllowForAlignment. +Layout/ExtraSpacing: + Enabled: false + +Metrics/LineLength: + Description: Limit lines to 80 characters. + StyleGuide: https://github.com/bbatsov/ruby-style-guide#80-character-limits + Enabled: true + Max: 130 + +# Configuration parameters: CountComments. +Metrics/ClassLength: + Enabled: false + Max: 400 + +Metrics/AbcSize: + Enabled: false + Max: 50 + +Metrics/MethodLength: + Enabled: false + Max: 50 + +# Configuration parameters: CountKeywordArgs. +Metrics/ParameterLists: + Max: 7 + +Style/BlockComments: + Enabled: false + +Style/ColonMethodCall: + Enabled: false + +# if you find "a == 3" readable and "3 == a" 'unreadable', do not contribute to this project. +Style/YodaCondition: + Enabled: false + +# Configuration parameters: EnforcedStyle, SupportedStyles. +Style/FormatString: + Enabled: false + +# Offense count: 1 +Metrics/CyclomaticComplexity: + Enabled: false + Max: 11 + +# Offense count: 1 +Metrics/PerceivedComplexity: + Enabled: false + Max: 14 + +# Cop supports --auto-correct. +Style/RedundantSelf: + Enabled: false + +# because apostrophes +Style/StringLiterals: + Enabled: false + +Style/TrailingCommaInLiteral: + Enabled: false diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..c8b70c5c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +sudo: false +language: ruby + +rvm: + - "2.0.0" + - "2.1.0" + - "2.2.0" + - rbx + +before_install: gem install bundler -v 1.15.4 +script: + - bundle exec rubocop --version + - bundle exec rubocop -D . + - bundle exec rspec diff --git a/.yardopts b/.yardopts new file mode 100644 index 00000000..7b700680 --- /dev/null +++ b/.yardopts @@ -0,0 +1 @@ +lib/arduino_ci.rb lib/**/*.rb diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..d6bbd532 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,27 @@ +# Change Log +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + +## [Unreleased] +### Added + +### Changed + +### Deprecated + +### Removed + +### Fixed + +### Security + + +## [0.0.1] - 2018-01-10 +### Added +- Skeleton for gem with working unit tests + + +[Unreleased]: https://github.com/ifreecarve/arduino_ci/compare/v0.0.1...HEAD +[0.0.1]: https://github.com/ifreecarve/arduino_ci/compare/v0.0.0...v0.0.1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..dde6db80 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,29 @@ +# Contributing to the ArduinoCI gem + +ArduinoCI uses a very standard GitHub workflow. + +1. Fork the repository on github +2. Make your desired changes +3. Push to your personal fork +4. Open a pull request + +Pull requests will trigger a Travis CI job. The following two commands will be expected to pass (so you may want to run them locally before opening the pull request): + + * `rubocop -D` - code style tests + * `rspec` - functional tests + +Be prepared to write tests to accompany any code you would like to see merged. + + +## Packaging the Gem + +* Merge pull request with new features +* Bump the version in lib/arduino_ci/version.rb and change it in README.md (since rubydoc.info doesn't always redirect to the latest version) +* Update the sections of `CHANGELOG.md` +* `git add README.md CHANGELOG.md lib/arduino_ci/version.rb` +* `git commit -m "vVERSION bump"` +* `git tag -a vVERSION -m "Released version VERSION"` +* `gem build arduino_ci.gemspec` +* `gem push arduino_ci-VERSION.gem` +* `git push upstream` +* `git push upstream --tags` diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..c88b26e7 --- /dev/null +++ b/Gemfile @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } + +# Specify your gem's dependencies in arduino_ci.gemspec +gemspec diff --git a/README.md b/README.md index e925430f..238a9c8b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,39 @@ -# arduino_ci -A Ruby gem for executing Continuous Integration (CI) tests on an Arduino library +[![Gem Version](https://badge.fury.io/rb/arduino_ci.svg)](https://rubygems.org/gems/arduino_ci) +[![Build Status](https://travis-ci.org/ifreecarve/arduino_ci.svg)](https://travis-ci.org/ifreecarve/arduino_ci) +[![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://www.rubydoc.info/gems/arduino_ci/) + +# ArduinoCI Ruby gem (`arduino_ci`) + +[Arduino CI](https://github.com/ifreecarve/arduino_ci) is a Ruby gem for executing Continuous Integration (CI) tests on an Arduino library -- both locally and as part of a service like Travis CI. + + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'arduino_ci' +``` + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install arduino_ci + + +## Usage + +TODO: Write usage instructions here, based on other TODO of writing the actual gem. + + +## Author + +This gem was written by Ian Katz (ifreecarve@gmail.com) in 2018. It's released under the Apache 2.0 license. + + +## See Also + +* [Contributing](CONTRIBUTING.md) diff --git a/arduino_ci.gemspec b/arduino_ci.gemspec new file mode 100644 index 00000000..fdb41385 --- /dev/null +++ b/arduino_ci.gemspec @@ -0,0 +1,27 @@ +# coding: utf-8 +lib = File.expand_path("../lib", __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require "arduino_ci/version" + +Gem::Specification.new do |spec| + spec.name = "arduino_ci" + spec.version = ArduinoCI::VERSION + spec.licenses = ['Apache 2.0'] + spec.authors = ["Ian Katz"] + spec.email = ["ifreecarve@gmail.com"] + + spec.summary = "Tools for building and unit testing Arduino libraries" + spec.description = spec.description + spec.homepage = "http://github.com/ifreecarve/arduino_ci" + + spec.files = ['README.md', '.yardopts'] + Dir['lib/**/*.*'].reject { |f| f.match(%r{^(test|spec|features)/}) } + + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] + + spec.add_development_dependency "bundler", "~> 1.15" + spec.add_development_dependency "rspec", "~> 3.0" + spec.add_development_dependency 'rubocop', '~> 0', '>= 0.46.0' + spec.add_development_dependency 'yard', '~>0.8', '>= 0.8' +end diff --git a/bin/console b/bin/console new file mode 100755 index 00000000..d6e8b5fa --- /dev/null +++ b/bin/console @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby + +require "bundler/setup" +require "arduino_ci" + +# You can add fixtures and/or initialization code here to make experimenting +# with your gem easier. You can also use a different console, if you like. + +# (If you use this, don't forget to add pry to your Gemfile!) +# require "pry" +# Pry.start + +require "irb" +IRB.start(__FILE__) diff --git a/bin/setup b/bin/setup new file mode 100755 index 00000000..dce67d86 --- /dev/null +++ b/bin/setup @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install + +# Do any other automated setup that you need to do here diff --git a/lib/arduino_ci.rb b/lib/arduino_ci.rb new file mode 100644 index 00000000..fc58630d --- /dev/null +++ b/lib/arduino_ci.rb @@ -0,0 +1,123 @@ +require "arduino_ci/version" + +require 'singleton' + +# Cross-platform way of finding an executable in the $PATH. +# via https://stackoverflow.com/a/5471032/2063546 +# which('ruby') #=> /usr/bin/ruby +def which(cmd) + exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] + ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| + exts.each do |ext| + exe = File.join(path, "#{cmd}#{ext}") + return exe if File.executable?(exe) && !File.directory?(exe) + end + end + nil +end + +# ArduinoCI contains classes for automated testing of Arduino code on the command line +# @author Ian Katz +module ArduinoCI + + # Wrap the Arduino executable. This requires, in some cases, a faked display. + class ArduinoCmd + + # create as many ArduinoCmds as you like, but we need one and only one display manager + class DisplayMgr + include Singleton + attr_reader :enabled + + def initialize + @existing = existing_display? + @enabled = false + @pid = nil + end + + # attempt to determine if the machine is running a graphical display (i.e. not Travis) + def existing_display? + return true if RUBY_PLATFORM.include? "darwin" + return true if ENV["DISPLAY"].include? ":" + false + end + + # enable a virtual display + def enable + return @enabled = true if @existing # silent no-op if built in display + return unless @pid.nil? + + @enabled = true + @pid = fork do + puts "Forking Xvfb" + system("Xvfb", ":1", "-ac", "-screen", "0", "1280x1024x16") + puts "Xvfb unexpectedly quit!" + end + sleep(3) # TODO: test a connection to the X server? + end + + # disable the virtual display + def disable + return @enabled = false if @existing # silent no-op if built in display + return if @pid.nil? + + begin + Process.kill 9, @pid + ensure + Process.wait @pid + @pid = nil + end + puts "Xvfb killed" + end + + # Enable a virtual display for the duration of the given block + def with_display + enable + begin + yield environment + ensure + disable + end + end + + def environment + return nil unless @existing || @enabled + return {} if @existing + { DISPLAY => ":1.0" } + end + + # On finalize, ensure child process is ended + def self.finalize + disable + end + end + + class << self + protected :new + + # attempt to find a workable Arduino executable across platforms + def guess_executable_location + osx_place = "/Applications/Arduino.app/Contents/MacOS/Arduino" + places = { + "arduino" => !which("arduino").nil?, + osx_place => (File.exist? osx_place), + } + places.each { |k, v| return k if v } + nil + end + + def autolocate + ret = new + ret.path = guess_executable_location + ret + end + end + + attr_accessor :path + + def initialize + @display_mgr = DisplayMgr::instance + end + + end + +end diff --git a/lib/arduino_ci/version.rb b/lib/arduino_ci/version.rb new file mode 100644 index 00000000..e16b7477 --- /dev/null +++ b/lib/arduino_ci/version.rb @@ -0,0 +1,3 @@ +module ArduinoCI + VERSION = "0.0.1".freeze +end diff --git a/spec/arduino_ci_spec.rb b/spec/arduino_ci_spec.rb new file mode 100644 index 00000000..0f3d9c57 --- /dev/null +++ b/spec/arduino_ci_spec.rb @@ -0,0 +1,7 @@ +require "spec_helper" + +RSpec.describe ArduinoCI do + it "has a version number" do + expect(ArduinoCI::VERSION).not_to be nil + end +end diff --git a/spec/arduino_exec_spec.rb b/spec/arduino_exec_spec.rb new file mode 100644 index 00000000..19d2147a --- /dev/null +++ b/spec/arduino_exec_spec.rb @@ -0,0 +1,30 @@ +require "spec_helper" + +RSpec.describe ArduinoCI::ArduinoCmd do + it "Finds the Arduino executable" do + arduino_cmd = ArduinoCI::ArduinoCmd.autolocate + # expect(arduino_cmd.path).not_to be nil + end +end + +RSpec.describe ArduinoCI::ArduinoCmd::DisplayMgr do + context "singleton ::instance" do + it "produces an instance" do + expect(ArduinoCI::ArduinoCmd::DisplayMgr::instance).not_to be_nil + end + end + + context "with_display" do + it "Properly enables and disables" do + manager = ArduinoCI::ArduinoCmd::DisplayMgr::instance + expect(manager.enabled).to be false + manager.with_display do |environment| + expect(manager.enabled).to be true + expect(environment.class).to eq(Hash) + also_manager = ArduinoCI::ArduinoCmd::DisplayMgr::instance + expect(also_manager.enabled).to be true + end + expect(manager.enabled).to be false + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 00000000..77b061d4 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,14 @@ +require "bundler/setup" +require "arduino_ci" + +RSpec.configure do |config| + # Enable flags like --only-failures and --next-failure + config.example_status_persistence_file_path = ".rspec_status" + + # Disable RSpec exposing methods globally on `Module` and `main` + config.disable_monkey_patching! + + config.expect_with :rspec do |c| + c.syntax = :expect + end +end From a711fd98fc209b884f03ed64088e81418ed084b8 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Wed, 10 Jan 2018 08:42:42 -0500 Subject: [PATCH 2/3] only build on one ruby version until we get CI sorted --- .travis.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index c8b70c5c..08dae787 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,10 +2,11 @@ sudo: false language: ruby rvm: - - "2.0.0" - - "2.1.0" - - "2.2.0" - - rbx +# - "2.0.0" +# - "2.1.0" +# - "2.2.0" +# - rbx + - "2.5.0" before_install: gem install bundler -v 1.15.4 script: From 8c7979043c2acde39ac1ac65c4c4971e87ea6b44 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Wed, 10 Jan 2018 08:44:36 -0500 Subject: [PATCH 3/3] anticipate that display variable may be unset --- lib/arduino_ci.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/arduino_ci.rb b/lib/arduino_ci.rb index fc58630d..7edf9596 100644 --- a/lib/arduino_ci.rb +++ b/lib/arduino_ci.rb @@ -37,6 +37,7 @@ def initialize # attempt to determine if the machine is running a graphical display (i.e. not Travis) def existing_display? return true if RUBY_PLATFORM.include? "darwin" + return true if ENV["DISPLAY"].nil? return true if ENV["DISPLAY"].include? ":" false end