diff --git a/.travis.yml b/.travis.yml index 83b319a..384fc3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,4 +5,4 @@ rvm: matrix: allow_failures: - rvm: 2.0.0 -script: "bundle exec rake" +script: "bundle exec rake ci" diff --git a/Guardfile b/Guardfile index 2486d8a..2858053 100644 --- a/Guardfile +++ b/Guardfile @@ -1,6 +1,3 @@ -# A sample Guardfile -# More info at https://github.com/guard/guard#readme - guard 'bundler' do watch('Gemfile') watch(/^.+\.gemspec/) @@ -15,20 +12,7 @@ guard 'rspec' do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/vagrant-lxc/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" } watch('spec/unit_helper.rb') { "spec/unit" } + watch('spec/acceptance_helper.rb') { "spec/acceptance" } watch('spec/spec_helper.rb') { "spec/" } - - # Rails example - watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } - watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } - watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] } - watch(%r{^spec/support/(.+)\.rb$}) { "spec" } - watch('config/routes.rb') { "spec/routing" } - watch('app/controllers/application_controller.rb') { "spec/controllers" } - - # Capybara features specs - watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" } - - # Turnip features and steps - watch(%r{^spec/acceptance/(.+)\.feature$}) - watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' } + watch(%r{^spec/support/(.+)\.rb$}) { "spec/" } end diff --git a/README.md b/README.md index db08a31..35351c9 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,13 @@ If want to develop from your physical machine, just sing that same old song: git clone git://github.com/fgrehm/vagrant-lxc.git cd vagrant-lxc bundle install -bundle exec rake # to run all specs +bundle exec rake # to run unit specs +``` + +To run acceptance specs, you'll have to ssh into one of the [development boxes](development/Vagrantfile) and run: + +``` +bundle exec rake spec:acceptance ``` To build the provided quantal64 box: diff --git a/spec/Vagrantfile b/spec/Vagrantfile new file mode 100644 index 0000000..67d553a --- /dev/null +++ b/spec/Vagrantfile @@ -0,0 +1,33 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +def local_apt_cache(box_name) + cache_dir = File.join(File.expand_path(Vagrant::Environment::DEFAULT_HOME), + 'cache', + 'apt', + box_name) + partial_dir = File.join(cache_dir, 'partial') + FileUtils.mkdir_p(partial_dir) unless File.exists? partial_dir + cache_dir +end + +Vagrant.require_plugin 'vagrant-lxc' + +Vagrant.configure("2") do |config| + config.vm.box = "quantal64" + config.vm.hostname = 'lxc-test-box' + + config.vm.box_url = 'http://dl.dropbox.com/u/13510779/lxc-quantal64-2013-04-10.box' + # Uncomment to test boxes built locally: + # config.vm.box_url = '../boxes/output/lxc-quantal64.box' + + cache_dir = local_apt_cache(config.vm.box) + config.vm.synced_folder cache_dir, "/var/cache/apt/archives", id: "vagrant-apt-cache" + + config.vm.provision :shell, + inline: 'mkdir -p /vagrant/tmp && echo -n "Provisioned" > /vagrant/tmp/provisioning' + + config.vm.network :forwarded_port, guest: 80, host: 8080 + config.vm.provision :shell, + inline: 'sudo apt-get install apache2 -y' +end diff --git a/spec/acceptance/sanity_check_spec.rb b/spec/acceptance/sanity_check_spec.rb new file mode 100644 index 0000000..296ee55 --- /dev/null +++ b/spec/acceptance/sanity_check_spec.rb @@ -0,0 +1,74 @@ +require 'acceptance_helper' + +describe 'Sanity check' do + after(:all) { destroy_container } + + context 'running `vagrant up` from scratch' do + before(:all) do + destroy_container + vagrant_up + end + + it 'creates a container' do + containers = `sudo lxc-ls`.chomp.split(/\s+/).uniq + expect(containers).to include vagrant_container_name + end + + it 'starts the newly created container' do + status = `sudo lxc-info -n #{vagrant_container_name}` + expect(status).to include 'RUNNING' + end + + it "is able to be SSH'ed" do + expect(vagrant_ssh('hostname')).to eq 'lxc-test-box' + end + + it 'mounts shared folders with the right permissions' do + vagrant_ssh 'mkdir -p /vagrant/tmp && echo -n "Shared" > /vagrant/tmp/shared' + shared_file_contents = File.read('/vagrant/spec/tmp/shared') + expect(shared_file_contents).to eq 'Shared' + end + + it 'provisions the container based on Vagrantfile configs' do + provisioned_file_contents = File.read('/vagrant/spec/tmp/provisioning') + expect(provisioned_file_contents).to eq 'Provisioned' + end + + it 'forwards configured ports' do + output = `curl -s localhost:8080`.strip.chomp + expect(output).to include 'It works!' + end + end + + context '`vagrant halt` on a running container' do + before(:all) do + destroy_container + vagrant_up + vagrant_halt + end + + it 'shuts down container' do + status = `sudo lxc-info -n #{vagrant_container_name}` + expect(status).to include 'STOPPED' + end + + it 'clears forwarded ports' do + `curl -s localhost:8080 --connect-timeout 2` + expect($?.exitstatus).to_not eq 0 + end + end + + context '`vagrant destroy`' do + before(:all) do + destroy_container + vagrant_up + @container_name = vagrant_container_name + vagrant_destroy + end + + it 'destroys the underlying container' do + containers = `sudo lxc-ls`.chomp.split(/\s+/).uniq + expect(containers).to_not include @container_name + end + end +end diff --git a/spec/acceptance/support/acceptance_example_group.rb b/spec/acceptance/support/acceptance_example_group.rb new file mode 100644 index 0000000..e363839 --- /dev/null +++ b/spec/acceptance/support/acceptance_example_group.rb @@ -0,0 +1,56 @@ +module AcceptanceExampleGroup + def self.included(base) + base.metadata[:type] = :acceptance + end + + ID_FILE = "/vagrant/spec/.vagrant/machines/default/lxc/id" + def vagrant_container_name + File.read(ID_FILE).strip.chomp if File.exists?(ID_FILE) + end + + def destroy_container + if name = vagrant_container_name + `sudo lxc-shutdown -n #{name} 2>/dev/null` + `sudo lxc-wait -n #{name} --state STOPPED 2>/dev/null` + `sudo lxc-destroy -n #{name} 2>/dev/null` + end + `sudo killall -9 redir 2>/dev/null` + end + + def with_vagrant_environment + opts = { cwd: 'spec', ui_class: TestUI } + env = Vagrant::Environment.new(opts) + yield env + env.unload + end + + def vagrant_up + with_vagrant_environment do |env| + env.cli('up', '--provider', 'lxc') + end + end + + def vagrant_halt + with_vagrant_environment do |env| + env.cli('halt') + end + end + + def vagrant_destroy + with_vagrant_environment do |env| + env.cli('destroy', '-f') + end + end + + def vagrant_ssh(cmd) + output = nil + with_vagrant_environment do |env| + result = env.cli('ssh', '-c', cmd) + if result.to_i != 0 + raise "SSH command failed: '#{cmd}'\n#{env.ui.messages.inspect}" + end + output = env.ui.messages[:info].join("\n").chomp + end + output + end +end diff --git a/spec/acceptance/support/machine_ext.rb b/spec/acceptance/support/machine_ext.rb new file mode 100644 index 0000000..d09fcff --- /dev/null +++ b/spec/acceptance/support/machine_ext.rb @@ -0,0 +1,13 @@ +# Monkey patch vagrant in order to reuse the UI test object that is set on +# our Vagrant::Environments +# +# TODO: Find out if this makes sense to be on vagrant core itself +require 'vagrant/machine' +Vagrant::Machine.class_eval do + alias :old_action :action + + define_method :action do |name, extra_env = nil| + extra_env = { ui: @env.ui }.merge(extra_env || {}) + old_action name, extra_env + end +end diff --git a/spec/acceptance/support/test_ui.rb b/spec/acceptance/support/test_ui.rb new file mode 100644 index 0000000..5f9a84c --- /dev/null +++ b/spec/acceptance/support/test_ui.rb @@ -0,0 +1,22 @@ +class TestUI < Vagrant::UI::Interface + attr_reader :messages + + METHODS = [:clear_line, :report_progress, :warn, :error, :info, :success] + + def initialize(resource = nil) + super + @messages = METHODS.each_with_object({}) { |m, h| h[m] = [] } + end + + def ask(*args) + super + # Automated tests should not depend on user input, obviously. + raise Errors::UIExpectsTTY + end + + METHODS.each do |method| + define_method(method) do |message, *opts| + @messages[method].push message + end + end +end diff --git a/spec/acceptance_helper.rb b/spec/acceptance_helper.rb new file mode 100644 index 0000000..ff45967 --- /dev/null +++ b/spec/acceptance_helper.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +unless ENV['USER'] == 'vagrant' + puts 'Acceptance specs are supposed to run from one of the vagrant-lxc dev machines' + exit 1 +end + +if defined? SimpleCov + SimpleCov.command_name 'acceptance' +end + +require 'vagrant' +require 'vagrant-lxc' + +Dir[File.dirname(__FILE__) + "/acceptance/support/**/*.rb"].each { |f| require f } + +RSpec.configure do |config| + config.include AcceptanceExampleGroup, :type => :acceptance, :example_group => { + :file_path => /\bspec\/acceptance\// + } +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 07038e7..bdbc54b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,14 +2,8 @@ if ENV['COVERAGE'] require 'simplecov' require 'coveralls' - SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ - SimpleCov::Formatter::HTMLFormatter, - Coveralls::SimpleCov::Formatter - ] - - SimpleCov.start do - add_filter { |source_file| source_file.filename =~ /\/spec\/support/ } - end + SimpleCov.start { add_filter '/spec/' } + SimpleCov.merge_timeout 300 end require 'bundler/setup' diff --git a/spec/support/.gitkeep b/spec/support/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/spec/support/unit_example_group.rb b/spec/unit/support/unit_example_group.rb similarity index 100% rename from spec/support/unit_example_group.rb rename to spec/unit/support/unit_example_group.rb diff --git a/spec/unit_helper.rb b/spec/unit_helper.rb index 9104290..4122faf 100644 --- a/spec/unit_helper.rb +++ b/spec/unit_helper.rb @@ -1,5 +1,11 @@ require 'spec_helper' +Dir[File.dirname(__FILE__) + "/unit/support/**/*.rb"].each { |f| require f } + +if defined? SimpleCov + SimpleCov.command_name 'unit' +end + RSpec.configure do |config| config.include RSpec::Fire diff --git a/tasks/spec.rake b/tasks/spec.rake index 8b2f3de..ddd4867 100644 --- a/tasks/spec.rake +++ b/tasks/spec.rake @@ -1,12 +1,15 @@ begin require 'rspec/core/rake_task' + require 'coveralls/rake/task' - # TODO: add 'spec:acceptance' and 'spec:integration' then they are in place desc 'Run all specs' - task :spec => ['spec:unit'] + task :spec => ['spec:unit', 'spec:acceptance'] desc 'Default task which runs all specs with code coverage enabled' - task :default => ['spec:set_coverage', 'spec'] + task :default => ['spec:set_coverage', 'spec:unit'] + + Coveralls::RakeTask.new + task :ci => ['spec:set_coverage', 'spec:unit', 'coveralls:push'] rescue LoadError; end namespace :spec do