aboutsummaryrefslogtreecommitdiffstats
path: root/vagrant/test.rb
blob: e7a08f67b771e0df7189de1c56d0848f5b054f2f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/usr/bin/env ruby
HELP = <<EOS
Runs the bindfs test suite in all vagrant boxes in parallel.

Usage: test.rb [--nohalt] [vm1 [...]]
Options:
  -h      --help            Print this and exit.
  --nohalt                  Don't halt VMs when done.
  --print-logs              Prints each VM's logs when it completes.

If VM names are given, only those are tested.

Bugs:
  - Spurious "Connection to 127.0.0.1 closed." messages can appear, and the
    terminal may need a `reset` after this command finishes.
EOS

require 'fileutils'

Dir.chdir(File.dirname(__FILE__))

halt_vms = true
print_logs = false
specifically_selected_vms = []
ARGV.each do |arg|
  if arg == '-h' || arg == '--help'
    puts HELP
    exit
  elsif arg == '--nohalt'
    halt_vms = false
  elsif arg == '--print-logs'
    print_logs = true
  elsif !arg.start_with?('-')
    specifically_selected_vms << arg
  else
    raise "Unknown option: #{arg}"
  end
end

dirs = Dir.glob('*/Vagrantfile').map { |path| File.dirname(path) }
unless specifically_selected_vms.empty?
  dirs = dirs.select { |dir| specifically_selected_vms.include?(dir) }
end

def with_retries(n, options = {}, &block)
  options = {
    sleep: 0,
    sleep_jitter: 0,
  }.merge options
  loop do
    begin
      return block.call
    rescue
      raise $! if n <= 0
      puts "Retrying #{n} more times after catching: #{$!}"
    end
    sleep(options[:sleep] + (Random.rand - 0.5) * options[:sleep_jitter])
    n -= 1
  end
end

puts "Running #{dirs.size} VMs in parallel: #{dirs.join(' ')}"
puts "You can follow the progress of each VM by tailing vagrant/*/test.log"
puts "Note: if your terminal goes wonky after this command, type 'reset'"
mutex = Thread::Mutex.new  # protects `$stdout` and `errors`
errors = []
threads = dirs.map do |dir|
  Thread.new do
    begin
      File.open(dir + "/test.log", "w+b") do |logfile|
        run_and_log = -> (command) do
          logfile.puts ""
          logfile.puts "##### #{command} #####"
          logfile.flush
          pid = Process.spawn(command, chdir: dir, out: logfile, err: logfile)
          Process.waitpid(pid)
          $?.success?
        end
        # parallel `vagrant up` commands can be flaky with VirtualBox due to lock contention,
        # so give it a few retries.
        with_retries(3, sleep: 1.0, sleep_jitter: 0.5) do
          unless run_and_log.call "vagrant up"
            raise "vagrant up failed"
          end
        end
        unless run_and_log.call "vagrant rsync"
          raise "vagrant rsync failed"
        end
        unless run_and_log.call "vagrant ssh -c 'cd /bindfs && sudo rm -Rf tests/tmp_test_bindfs && ./configure && make distclean && ./configure && make && make check && sudo make check'"
          mutex.synchronize do
            errors << "VM #{dir} tests failed."
          end
        end
        if halt_vms
          unless run_and_log.call "vagrant halt"
            raise "vagrant halt failed"
          end
        end
      end
    rescue
      mutex.synchronize do
        errors << "VM #{dir} error: #{$!}"
      end
    ensure
      mutex.synchronize do
        puts "Finished VM: #{dir}"
      end
      if print_logs
        puts File.read(dir + "/test.log")
        puts
      end
    end
  end
end

threads.each(&:join)

if errors.empty?
  puts "All OK"
else
  unless errors.empty?
    puts
    puts "Errors:"
    errors.each { |err| puts "  #{err}" }
    puts
    puts "See test.log in a failed VM's directory for more information"
    puts
    exit 1
  end
end