Top Level Namespace

Defined Under Namespace

Classes: MySQLReport, Result, ServiceAPI, SimpleFileReport, State, String

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args) ⇒ Object


Finally, add missing_method hook to enable test suite DSL to call test cases by their names.

Note: In some ruby versions (at least 1.9.0) we can use File.exists? here instead of having to keep the list of test files in $TEST_FILES and checking that. However, in some other ruby versions (at least 1.9.2p290) File.exists? ends up invoking some non-existent methods which trigger a method_missing call, which leads to an infinite loop (until stack space runs out) if method_missing relies on File.exists?.



368
369
370
371
372
373
374
375
376
# File 'lib/restest.rb', line 368

def method_missing(method_name, *args)
  if ($TEST_FILES[method_name.to_s])
    run_test(method_name)
  elsif (method_name == :ignore) && $TEST_FILES[args[0].to_s]
    run_test(args[0], true)
  else
    super
  end
end

Instance Method Details

#die(line) ⇒ Object


Utility method to show a line of output and exit. Use when a fatal misconfiguration or error is seen.



37
38
39
40
# File 'lib/restest.rb', line 37

def die(line)
  puts "[ERROR] #{line}"
  exit(1)
end

#get(name) ⇒ Object


Allow test scripts to get values from the global state.



54
55
56
# File 'lib/restest.rb', line 54

def get(name)
  return $GLOBAL_STATE.get(name)
end

#load_test(name, state) ⇒ Object


Loads test file



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
# File 'lib/restest.rb', line 137

def load_test(name, state)
  if (!File.exists?("tests/#{name}"))
    die("No test file #{name}")
  end

  state.set_in_test('filename', name)
  file = File.new("tests/#{name}", "r")
  doc = false
  out(3, "Reading test file tests/#{name}")

  # line 1: service-class-name api-name
  line = file.gets
  line =~ /(\S*)\s*(\S*)/
  if (!$1 || !$2)
    die("Test #{name} missing service and/or API")
  end
  state.set_in_test('service', $1)
  state.set_in_test('api', $2)

  # then process 'set' or 'validate' directives
  while ((line = file.gets) != nil)
    next if line =~ /^\s*$/
    next if line =~ /^#/

    if (line =~ /^set (\S*)\s+=\s+(.*)/)
      state.set_in_test($1, $2)

    elsif (line =~ /^validate (\S+)\s+(\S+)\s+(.*)/)
      state.set_validate($1, "#{$2} #{$3}")

    elsif (line =~ /^doc (.*)/)
      state.set_in_test('doc', $1)
      doc = true

    else
      die("Unknown directive [#{line}] in #{name}")
    end
  end

  if (!doc)
    die("Test #{name} has no documentation (doc line)")
  end
end

#out(level, line) ⇒ Object


General purpose log output. Log level is zero by default, increased by one for each -v argument.



45
46
47
48
49
# File 'lib/restest.rb', line 45

def out(level, line)
  if ($LOG_LEVEL >= level)
    puts line
  end
end

#respond_to?(method_name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


378
379
380
381
382
383
384
# File 'lib/restest.rb', line 378

def respond_to?(method_name, include_private = false)
  if ($TEST_FILES[method_name.to_s])
    return true
  else
    super
  end
end

#restore_state(tag) ⇒ Object


Restore global state to a previously saved state.



82
83
84
85
86
87
# File 'lib/restest.rb', line 82

def restore_state(tag)
  $GLOBAL_STATE.vars = $SAVED_STATES[tag]
  if ($GLOBAL_STATE.vars == nil)
    die("Attempted to restore to a saved stage [#{tag}] which does not exist!")
  end
end

#run_suite(name) ⇒ Object


Run a separate test suite file as part of current test suite. The global state is saved and then restored to isolate state changes made by the included test suite.



126
127
128
129
130
131
132
# File 'lib/restest.rb', line 126

def run_suite(name)
  out(1, "\n\n--Running included test suite #{name}")
  save_state("_pre_run_suite")
  load(name)
  restore_state("_pre_run_suite")
  out(1, "\n--Completed included test suite #{name}")
end

#run_test(name, ignore = false) ⇒ Object


Entry point for running one test. Called from test script by invoking the name of the test file (see method_missing)



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/restest.rb', line 184

def run_test(name, ignore=false)
  out(1, "\n\n\nRunning test #{name}")

  if ($LOG_LEVEL > 0)
    save_state('_internal_trace')
  end

  test_state = State.new($GLOBAL_STATE)
  load_test(name, test_state)

  service = test_state.get('service')
  api = test_state.get('api')
  out(1, "Service class: [#{service}] Calling API: [#{api}]")

  if ($INTERACTIVE)
    print "Interactive mode... <enter> to run this test now, <c>ontinue: "
    STDOUT.flush
    input = gets.chomp!
    $INTERACTIVE = false if (input == "c")
  end

  before = Time.now()

  allow_retries = test_state.get('allow_retries')
  if (allow_retries != nil)
    times = allow_retries.to_i
    sleep_sec = test_state.get('retry_sleep').to_i
    if (sleep_sec < 1)
      die("allow_retries is set but retry_sleep is not")
    end
    out(1, "Test allow retries: allow_retries: #{times}  retry_sleep: #{sleep_sec}")
    begin
      out(1, "Tries left: #{times}")
      test_state = State.new($GLOBAL_STATE)
      load_test(name, test_state)
      testobj = Object::const_get(service).new
      result = testobj.send(api, test_state)
      times -= 1
      sleep(sleep_sec) if !result.is_ok
    end while (times > 0 && !result.is_ok && result.allow_retry)

  else
    testobj = Object::const_get(service).new
    result = testobj.send(api, test_state)
  end

  if ($LOG_LEVEL > 0)
    show_state_diff('_internal_trace')
  end

  duration = Integer((Time.now() - before) * 1000)

  out(1, "Duration of test: #{duration} ms")

  prefix = ""
  if (ignore)
    prefix="IGNORE-"
  end

  if (result.is_ok)
    out(0, "[#{prefix}OK] (#{duration}ms) #{service}.#{api}: #{test_state.get('doc')}")
    $TESTS_OK += 1 if !ignore
  else
    out(0, "[#{prefix}FAIL] (#{duration}ms) #{service}.#{api}: #{test_state.get('doc')} #{result.message}")
    $TESTS_FAIL += 1 if !ignore
  end

  if ($REPORT != nil)
    if (result.is_ok)
      status = "#{prefix}OK"
    else
      status = "#{prefix}FAIL"
    end
    $REPORT.log(status, duration, service, api, name, result.message)
  end

  if (result.abort_suite_run)
    die("Test suite run has been aborted by previous test!")
  end
end

#save_state(tag) ⇒ Object


Saves the current state.



75
76
77
# File 'lib/restest.rb', line 75

def save_state(tag)
  $SAVED_STATES[tag] = $GLOBAL_STATE.vars.clone()
end

#set(name, value) ⇒ Object


Allow test scripts to set values in the global state.



61
62
63
# File 'lib/restest.rb', line 61

def set(name, value)
  $GLOBAL_STATE.set(name, value)
end

#show_state_diff(tag_a, tag_b = nil) ⇒ Object


Print out the difference from the state named by tag_a to the state named by tag_b (these are states previously saved with save_state(). If tag_b is not provided, diff is shown to the current global state.



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
# File 'lib/restest.rb', line 93

def show_state_diff(tag_a, tag_b = nil)

  a = $SAVED_STATES[tag_a]
  if (tag_b == nil)
    b = $GLOBAL_STATE.vars
  else
    b = $SAVED_STATES[tag_b]
  end

  if (a == nil || b == nil)
    puts "[ERROR] Unable to show diff from state #{tag_a} to state #{tag_b}"
    return
  end

  b.each { |k,v|
    if (a[k] == nil)
      puts "  ADDED:   '#{k}' => '#{v}'"
    elsif (a[k] != v)
      puts "  CHANGED: '#{k}' from '#{a[k]}' to '#{v}'"
    end
  }

  a.each { |k,v|
    if (b[k] == nil)
      puts "  REMOVED: '#{k}' was '#{a[k]}'"
    end
  }
end

#show_usage(opts) ⇒ Object


Show Usage message



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/restest.rb', line 268

def show_usage(opts)
  puts opts
  puts <<EOF

Running a test suite
====================

To run a test suite, simply run the test suite file as it is an
executable ruby script. A configuration file argument must be
provided.

The configuration file is responsible for setting state values which
are specific to a test environment, and thus not suitable to set
elsewhere. These include values such as hostnames, user names, test
domains, etc.

(TODO: Until the framework packaging is organized, you need to run it
from the test suite directory. So for the share tests for example, "cd
share-tests" first. This is a temporary limitation.)

The initial share test script is called "suite1" so run it by:

% ./suite1 -c config.ENV

(Where ENV is the share environment to run it against. There are
separate config files for each share env.)

EOF
end

#unset(name) ⇒ Object


Allow test scripts to remove values from the global state.



68
69
70
# File 'lib/restest.rb', line 68

def unset(name)
  $GLOBAL_STATE.unset(name)
end