Class: Toaster::TestRunner

Inherits:
Object
  • Object
show all
Defined in:
lib/toaster/test/test_runner.rb

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.delay_between_testsObject

Returns the value of attribute delay_between_tests.



29
30
31
# File 'lib/toaster/test/test_runner.rb', line 29

def delay_between_tests
  @delay_between_tests
end

.last_start_timeObject

Returns the value of attribute last_start_time.



29
30
31
# File 'lib/toaster/test/test_runner.rb', line 29

def last_start_time
  @last_start_time
end

.semaphoreObject

Returns the value of attribute semaphore.



29
30
31
# File 'lib/toaster/test/test_runner.rb', line 29

def semaphore
  @semaphore
end

.signalObject

Returns the value of attribute signal.



29
30
31
# File 'lib/toaster/test/test_runner.rb', line 29

def signal
  @signal
end

.singletonObject

Returns the value of attribute singleton.



29
30
31
# File 'lib/toaster/test/test_runner.rb', line 29

def singleton
  @singleton
end

Class Method Details

.ensure_automation_exists_in_db(automation_name, recipes, test_suite, destroy_container = true, print_output = false) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/toaster/test/test_runner.rb', line 101

def self.ensure_automation_exists_in_db(automation_name,
  recipes, test_suite, destroy_container=true, print_output=false)

  chef_node_name = ChefUtil.extract_node_name(automation_name)
  # prepare run list (add toaster::testing recipe definitions)
  actual_run_list = ChefUtil.prepare_run_list(chef_node_name, recipes)
  reduced_run_list = ChefUtil.get_reduced_run_list(actual_run_list)
  automation = Automation.find_by_cookbook_and_runlist(automation_name, reduced_run_list)
  return automation if !automation.nil?

  # if we don't have a matching automation in the DB yet, execute an initial run..
  c = TestCase.new(test_suite)
  test_suite.test_cases << c
  puts "INFO: Executing initial automation run; test case '#{c.uuid}'"
  automation_run = execute_test(c, destroy_container, print_output)
  puts "DEBUG: Finished execution of initial automation run; test case '#{c.uuid}': #{automation_run}"
  return nil if !automation_run
  return automation_run.automation
end

.execute_test(test_case, destroy_container = true, print_output = false, num_attempts = 2) ⇒ Object

execute the provided test case



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/toaster/test/test_runner.rb', line 124

def self.execute_test(test_case, destroy_container=true, 
    print_output=false, num_attempts=2)

  test_suite = nil
  test_id = nil
  recipes = nil
  automation = nil
  self.semaphore.synchronize do
    test_suite = test_case.test_suite
    automation = test_suite.automation
    test_id = test_suite.uuid
    recipes = automation.recipes
  end

  sleep_time = 0
  self.semaphore.synchronize do
    time = TimeStamp.now()
    diff = time - self.last_start_time
    if diff < self.delay_between_tests.to_f
      sleep_time = self.delay_between_tests.to_f - diff
    end
    self.last_start_time = time + sleep_time
  end

  # sleep a bit, and then this test is ready to go...
  sleep(sleep_time)
  time_now = TimeStamp.now()
  test_case.start_time = time_now

  # start test case execution
  automation_run = nil
  error_output = nil
  while num_attempts > 0
    begin
      automation_run = nil
      if automation.is_chef?
        # generate automation attributes which represent this test case
        chef_node_attrs = test_case.create_chef_node_attrs()
        # set cookbook if necessary
        automation.cookbook = automation.get_short_name if !automation.cookbook
        # now run test!
        automation_run = TestRunner.do_execute_test_chef(automation.cookbook,
            automation.script, recipes, chef_node_attrs, test_suite.lxc_prototype, 
            test_id, destroy_container, print_output)
      else
        raise "Unknown automation language/type: '#{automation.language}'"
      end

      automation_run.success = true
      test_case.test_suite().test_cases << test_case if !test_case.test_suite().test_cases().include?(test_case)
      test_case.automation_run = automation_run

      num_attempts = 0
    rescue Object => ex
      error_output = ex
      num_attempts -= 1
      puts "WARN: cannot run test case '#{test_case.uuid}' (remaining attempts: #{num_attempts}): #{ex}"
      puts "#{ex.backtrace.join("\n")}"
    end
  end

  if !automation_run
    machine_id = Util.get_machine_id()
    automation = test_case.test_suite.automation
    automation_run = AutomationRun.new(
      :automation => automation, 
      :machine_id => machine_id,
      :user => test_case.test_suite.user
    )
    puts "WARN: Test case '#{test_case.uuid}' failed entirely, storing " +
        "an empty automation run '#{test_case.automation_run}' with success=false."
    automation_run.success = false
    automation_run.end_time = TimeStamp.now
    automation_run.error_details = "Test case '#{test_case.uuid}' failed " +
        "(no automation run created by test runner). Output:\n#{error_output}"
    automation_run.save
    test_case.automation_run = automation_run
  end

  test_case.end_time = TimeStamp.now().to_i
  test_case.save

  return automation_run
end

.instanceObject

singleton pattern



92
93
94
95
96
97
98
99
# File 'lib/toaster/test/test_runner.rb', line 92

def self.instance
  self.semaphore.synchronize do
    if !self.singleton
      self.singleton = TestRunner.new()
    end
  end
  return singleton
end

Instance Method Details

#execute_test_case(test_case, do_wait_until_finished = true) ⇒ Object



75
76
77
# File 'lib/toaster/test/test_runner.rb', line 75

def execute_test_case(test_case, do_wait_until_finished=true)
  execute_tests(test_case, do_wait_until_finished)
end

#execute_tests(test_cases, do_wait_until_finished = true) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/toaster/test/test_runner.rb', line 78

def execute_tests(test_cases, do_wait_until_finished=true)
  test_cases = [test_cases] if !test_cases.kind_of?(Array)
  test_cases.each do |test|
    puts "INFO: Pushing test case to queue: #{test.uuid}"
    @request_queue.push(test)
  end

  if do_wait_until_finished
    puts "INFO: Waiting until #{test_cases.size} test cases are finished."
    wait_until_finished(test_cases)
  end
end

#schedule_test_case(test_case) ⇒ Object



72
73
74
# File 'lib/toaster/test/test_runner.rb', line 72

def schedule_test_case(test_case)
  execute_test_case(test_case, false)
end

#schedule_tests(test_suite, test_case_uuids) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/toaster/test/test_runner.rb', line 59

def schedule_tests(test_suite, test_case_uuids)
  test_cases = []
  test_case_uuids.each do |tc|
    if !tc.kind_of?(TestCase)
      tc = TestCase.find(:uuid => tc)[0]
    end
    test_cases << tc
  end
  test_cases.each do |tc|
    schedule_test_case(tc)
  end
end

#startObject



51
52
53
# File 'lib/toaster/test/test_runner.rb', line 51

def start()
  start_worker_threads()
end

#stopObject



55
56
57
# File 'lib/toaster/test/test_runner.rb', line 55

def stop()
  stop_threads()
end