mesa_test 0.2.12 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/mesa_test +262 -432
- data/lib/mesa_test.rb +438 -1158
- metadata +7 -8
data/lib/mesa_test.rb
CHANGED
|
@@ -11,9 +11,10 @@ require 'json'
|
|
|
11
11
|
MesaDirError = Class.new(StandardError)
|
|
12
12
|
TestCaseDirError = Class.new(StandardError)
|
|
13
13
|
InvalidDataType = Class.new(StandardError)
|
|
14
|
+
GitHubError = Class.new(StandardError)
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
GITHUB_HTTPS = 'https://github.com/MESAHub/mesa.git'.freeze
|
|
17
|
+
GITHUB_SSH = 'git@github.com:MESAHub/mesa.git'.freeze
|
|
17
18
|
|
|
18
19
|
class MesaTestSubmitter
|
|
19
20
|
DEFAULT_URI = 'https://mesa-test-hub.herokuapp.com'.freeze
|
|
@@ -28,7 +29,8 @@ shown in parentheses at the end of a prompt. Pressing enter will accept the
|
|
|
28
29
|
default values.
|
|
29
30
|
|
|
30
31
|
To submit to MESATestHub, a valid computer name, email address, and password
|
|
31
|
-
are all required.
|
|
32
|
+
are all required. To actually run a test, you need to specify a location for
|
|
33
|
+
your base MESA git repository. All other data are useful, but optional. Any data
|
|
32
34
|
transferred to MESATestHub will be encrypted via HTTPS, but be warned that your
|
|
33
35
|
e-mail and password will be stored in plain text.'
|
|
34
36
|
# Get computer name
|
|
@@ -36,11 +38,6 @@ e-mail and password will be stored in plain text.'
|
|
|
36
38
|
"(#{s.computer_name}):", :blue)
|
|
37
39
|
s.computer_name = response unless response.empty?
|
|
38
40
|
|
|
39
|
-
# Get user name
|
|
40
|
-
response = shell.ask 'What is the name of the operator of this ' \
|
|
41
|
-
"computer? (#{s.user_name}):", :blue
|
|
42
|
-
s.user_name = response unless response.empty?
|
|
43
|
-
|
|
44
41
|
# Get user e-mail
|
|
45
42
|
response = shell.ask 'What is the email you can be reached ' \
|
|
46
43
|
"at (required)? (#{s.email}):", :blue
|
|
@@ -51,40 +48,33 @@ e-mail and password will be stored in plain text.'
|
|
|
51
48
|
"#{s.email} (required)? (#{s.password})", :blue
|
|
52
49
|
s.password = response unless response.empty?
|
|
53
50
|
|
|
51
|
+
# Determine if we'll use ssh or https to access github
|
|
52
|
+
response = shell.ask 'When accessing GitHub, which protocol do you '\
|
|
53
|
+
'want to use? ', :blue, limited_to: %w[ssh https]
|
|
54
|
+
s.github_protocol = response.strip.downcase.to_sym
|
|
55
|
+
|
|
56
|
+
# Get location of source MESA repo (the mirror)
|
|
57
|
+
response = shell.ask "Where is/should your mirrored MESA repository " \
|
|
58
|
+
"located? This is where a mirror will be stored from which test " \
|
|
59
|
+
"repos will be generated. You won't touch this in regular operation. " \
|
|
60
|
+
"(#{s.mesa_mirror}):", :blue
|
|
61
|
+
s.mesa_mirror = response unless response.empty?
|
|
62
|
+
|
|
63
|
+
# Get location of source MESA work (where testing happens)
|
|
64
|
+
response = shell.ask "Where is/should your working directory for "\
|
|
65
|
+
"testing be located? This is where testing actually occurs, but all "\
|
|
66
|
+
"files it uses are cached in the mirror repo to save time later. " \
|
|
67
|
+
"(#{s.mesa_work}):", :blue
|
|
68
|
+
s.mesa_work = response unless response.empty?
|
|
69
|
+
|
|
54
70
|
# Get platform information
|
|
55
71
|
response = shell.ask 'What is the platform of this computer (eg. ' \
|
|
56
72
|
"macOS, Ubuntu)? (#{s.platform}):", :blue
|
|
57
73
|
s.platform = response unless response.empty?
|
|
58
|
-
response = shell.ask 'What is the version of the platform (eg. 10.
|
|
59
|
-
"16.04)? (#{s.platform_version}):", :blue
|
|
74
|
+
response = shell.ask 'What is the version of the platform (eg. 10.15.5, ' \
|
|
75
|
+
"Ubuntu 16.04)? (#{s.platform_version}):", :blue
|
|
60
76
|
s.platform_version = response unless response.empty?
|
|
61
77
|
|
|
62
|
-
# Get processor information
|
|
63
|
-
response = shell.ask 'What type of processor does this computer have ' \
|
|
64
|
-
"(eg. 3.1 GHz Intel i7)? (#{s.processor}):", :blue
|
|
65
|
-
s.processor = response unless response.empty?
|
|
66
|
-
|
|
67
|
-
# Get ram information
|
|
68
|
-
response = shell.ask 'How much RAM (in integer GB) does this computer ' \
|
|
69
|
-
"have (eg. 8)? (#{s.ram_gb}) ", :blue
|
|
70
|
-
s.ram_gb = response.to_i unless response.empty?
|
|
71
|
-
|
|
72
|
-
# Get compiler information
|
|
73
|
-
response = shell.ask "Which compiler are you using? (#{s.compiler}):",
|
|
74
|
-
:blue, limited_to: ['', 'SDK', 'gfortran', 'ifort']
|
|
75
|
-
s.compiler = response unless response.empty?
|
|
76
|
-
|
|
77
|
-
# Get compiler version
|
|
78
|
-
response = shell.ask 'What version of the compiler (eg. 20170921 or ' \
|
|
79
|
-
"7.2.0)? (#{s.compiler_version}): ", :blue
|
|
80
|
-
s.compiler_version = response unless response.empty?
|
|
81
|
-
|
|
82
|
-
# Get earliest revision to check
|
|
83
|
-
response = shell.ask "What's the earliest revision to search back to " \
|
|
84
|
-
'when finding the latest testable revision (eg. 10000)? ' \
|
|
85
|
-
"(#{s.last_tested}): ", :blue
|
|
86
|
-
s.last_tested = response.to_i unless response.empty?
|
|
87
|
-
|
|
88
78
|
# Confirm save location
|
|
89
79
|
response = shell.ask "This will be saved in #{s.config_file}. Press " \
|
|
90
80
|
'enter to accept or enter a new location:', :blue, path: true
|
|
@@ -95,15 +85,15 @@ e-mail and password will be stored in plain text.'
|
|
|
95
85
|
if confirm_computer_data
|
|
96
86
|
save_computer_data
|
|
97
87
|
else
|
|
98
|
-
|
|
88
|
+
shell.say "Restarting wizard.\n"
|
|
99
89
|
setup
|
|
100
90
|
end
|
|
101
91
|
end
|
|
102
92
|
|
|
103
93
|
def self.new_from_config(
|
|
104
|
-
config_file: File.join(ENV['HOME'], '.mesa_test.yml'),
|
|
94
|
+
config_file: File.join(ENV['HOME'], '.mesa_test', 'config.yml'),
|
|
95
|
+
force_setup: false,
|
|
105
96
|
base_uri: DEFAULT_URI
|
|
106
|
-
# base_uri: 'http://localhost:3000'
|
|
107
97
|
)
|
|
108
98
|
new_submitter = new(config_file: config_file, base_uri: base_uri)
|
|
109
99
|
if force_setup
|
|
@@ -117,22 +107,27 @@ e-mail and password will be stored in plain text.'
|
|
|
117
107
|
end
|
|
118
108
|
|
|
119
109
|
attr_accessor :computer_name, :user_name, :email, :password, :platform,
|
|
120
|
-
:
|
|
121
|
-
:
|
|
110
|
+
:mesa_mirror, :mesa_work, :platform_version, :processor,
|
|
111
|
+
:config_file, :base_uri, :last_tested, :github_protocol
|
|
122
112
|
|
|
123
113
|
attr_reader :shell
|
|
124
114
|
|
|
125
115
|
# many defaults are set in body
|
|
126
116
|
def initialize(
|
|
127
|
-
computer_name: nil, user_name: nil, email: nil,
|
|
128
|
-
|
|
129
|
-
|
|
117
|
+
computer_name: nil, user_name: nil, email: nil, github_protocol: nil,
|
|
118
|
+
mesa_mirror: nil, platform: nil, platform_version: nil, processor: nil,
|
|
119
|
+
config_file: nil, base_uri: nil, last_tested: nil
|
|
130
120
|
)
|
|
131
121
|
@computer_name = computer_name || Socket.gethostname.scan(/^[^\.]+\.?/)[0]
|
|
132
122
|
@computer_name.chomp!('.') if @computer_name
|
|
133
123
|
@user_name = user_name || (ENV['USER'] || ENV['USERNAME'])
|
|
134
124
|
@email = email || ''
|
|
135
125
|
@password = password || ''
|
|
126
|
+
@github_protocol = github_protocol || :ssh
|
|
127
|
+
@mesa_mirror = mesa_mirror ||
|
|
128
|
+
File.join(ENV['HOME'], '.mesa_test', 'mirror')
|
|
129
|
+
@mesa_work = mesa_work ||
|
|
130
|
+
File.join(ENV['HOME'], '.mesa_test', 'work')
|
|
136
131
|
@platform = platform
|
|
137
132
|
if @platform.nil?
|
|
138
133
|
@platform =
|
|
@@ -146,17 +141,15 @@ e-mail and password will be stored in plain text.'
|
|
|
146
141
|
end
|
|
147
142
|
@platform_version = platform_version || ''
|
|
148
143
|
@processor = processor || ''
|
|
149
|
-
@
|
|
150
|
-
|
|
151
|
-
@compiler_version = compiler_version || ''
|
|
152
|
-
@config_file = config_file || File.join(ENV['HOME'], '.mesa_test.yml')
|
|
144
|
+
@config_file = config_file || File.join(ENV['HOME'], '.mesa_test',
|
|
145
|
+
'config.yml')
|
|
153
146
|
@base_uri = base_uri
|
|
154
|
-
@last_tested = last_tested || DEFAULT_REVISION
|
|
155
147
|
|
|
156
148
|
# set up thor-proof way to get responses from user. Thor hijacks the
|
|
157
149
|
# gets command, so we have to use its built-in "ask" method, which is
|
|
158
150
|
# actually more useful
|
|
159
151
|
@shell = Thor::Shell::Color.new
|
|
152
|
+
|
|
160
153
|
yield self if block_given?
|
|
161
154
|
end
|
|
162
155
|
|
|
@@ -169,14 +162,12 @@ e-mail and password will be stored in plain text.'
|
|
|
169
162
|
puts 'Ready to submit the following data:'
|
|
170
163
|
puts '-------------------------------------------------------'
|
|
171
164
|
puts "Computer Name #{computer_name}"
|
|
172
|
-
puts "User Name #{user_name}"
|
|
173
165
|
puts "User email #{email}"
|
|
174
166
|
puts 'Password ***********'
|
|
167
|
+
puts "GitHub Protocol #{github_protocol}"
|
|
168
|
+
puts "MESA Mirror Location #{mesa_mirror}"
|
|
169
|
+
puts "MESA Work Location #{mesa_work}"
|
|
175
170
|
puts "Platform #{platform} #{platform_version}"
|
|
176
|
-
puts "Processor #{processor}"
|
|
177
|
-
puts "RAM #{ram_gb} GB"
|
|
178
|
-
puts "Compiler #{compiler} #{compiler_version}"
|
|
179
|
-
puts "Last tested revision #{last_tested}"
|
|
180
171
|
puts "Config location #{config_file}"
|
|
181
172
|
puts '-------------------------------------------------------'
|
|
182
173
|
puts ''
|
|
@@ -189,151 +180,107 @@ e-mail and password will be stored in plain text.'
|
|
|
189
180
|
# change platforms (i.e. switch from mac to linux, or change between linux
|
|
190
181
|
# flavors), you should create a new computer account. Similarly, create new
|
|
191
182
|
# computer accounts if you change your RAM or processor. You do not need
|
|
192
|
-
# to change computers if you upgrade your platform (macOS 10.12 -> 10.13
|
|
193
|
-
# if you try different compilers
|
|
194
|
-
#
|
|
195
|
-
# Note this is NOT checked! The server really only uses the test-by-test
|
|
196
|
-
# quantities (platform version, compiler, compiler version) and the
|
|
197
|
-
# computer name. Once the computer is found (by the name) all the other
|
|
198
|
-
# data is assumed to be fixed. The others... probably shouldn't be here,
|
|
199
|
-
# but remain so you can confirm that the computer on the web server is the
|
|
200
|
-
# same one you think you are working with locally.
|
|
183
|
+
# to change computers if you upgrade your platform (macOS 10.12 -> 10.13
|
|
201
184
|
def save_computer_data
|
|
202
185
|
data_hash = {
|
|
203
186
|
'computer_name' => computer_name,
|
|
204
|
-
'user_name' => user_name,
|
|
205
187
|
'email' => email,
|
|
206
188
|
'password' => password,
|
|
189
|
+
'github_protocol' => github_protocol,
|
|
190
|
+
'mesa_mirror' => mesa_mirror,
|
|
191
|
+
'mesa_work' => mesa_work,
|
|
207
192
|
'platform' => platform,
|
|
208
|
-
'processor' => processor,
|
|
209
|
-
'ram_gb' => ram_gb,
|
|
210
193
|
'platform_version' => platform_version,
|
|
211
|
-
'compiler' => compiler,
|
|
212
|
-
'compiler_version' => compiler_version,
|
|
213
|
-
'last_tested' => last_tested
|
|
214
194
|
}
|
|
195
|
+
# make sure there's a directory to write to
|
|
196
|
+
unless dir_or_symlink_exists? File.dirname(config_file)
|
|
197
|
+
FileUtils.mkdir_p File.dirname(config_file)
|
|
198
|
+
end
|
|
215
199
|
File.open(config_file, 'w') { |f| f.write(YAML.dump(data_hash)) }
|
|
216
200
|
end
|
|
217
201
|
|
|
218
202
|
def load_computer_data
|
|
219
203
|
data_hash = YAML.safe_load(File.read(config_file), [Symbol])
|
|
220
204
|
@computer_name = data_hash['computer_name']
|
|
221
|
-
@user_name = data_hash['user_name']
|
|
222
205
|
@email = data_hash['email']
|
|
223
206
|
@password = data_hash['password']
|
|
207
|
+
@github_protocol = data_hash['github_protocol'].to_sym
|
|
208
|
+
@mesa_mirror = data_hash['mesa_mirror']
|
|
209
|
+
@mesa_work = data_hash['mesa_work']
|
|
224
210
|
@platform = data_hash['platform']
|
|
225
|
-
@processor = data_hash['processor']
|
|
226
|
-
@ram_gb = data_hash['ram_gb']
|
|
227
211
|
@platform_version = data_hash['platform_version']
|
|
228
|
-
@compiler = data_hash['compiler']
|
|
229
|
-
@compiler_version = data_hash['compiler_version']
|
|
230
|
-
@last_tested = data_hash['last_tested'] || @last_tested
|
|
231
212
|
end
|
|
232
213
|
|
|
233
|
-
#
|
|
234
|
-
#
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
res = {
|
|
238
|
-
test_case: test_case.test_name,
|
|
239
|
-
mod: test_case.mod,
|
|
240
|
-
computer: computer_name,
|
|
214
|
+
# Parameters to be submitted in JSON format for reporting information about
|
|
215
|
+
# the submitting user and computer
|
|
216
|
+
def submitter_params
|
|
217
|
+
{
|
|
241
218
|
email: email,
|
|
242
219
|
password: password,
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
total_runtime_seconds: test_case.total_runtime_seconds,
|
|
246
|
-
mesa_version: test_case.mesa_version,
|
|
247
|
-
passed: test_case.passed? ? 1 : 0,
|
|
248
|
-
compiler: test_case.compiler || compiler,
|
|
249
|
-
compiler_version: test_case.compiler_version || compiler_version,
|
|
250
|
-
platform_version: platform_version,
|
|
251
|
-
omp_num_threads: test_case.test_omp_num_threads,
|
|
252
|
-
success_type: test_case.success_type,
|
|
253
|
-
failure_type: test_case.failure_type,
|
|
254
|
-
steps: test_case.steps,
|
|
255
|
-
retries: test_case.retries,
|
|
256
|
-
backups: test_case.backups,
|
|
257
|
-
diff: test_case.diff,
|
|
258
|
-
checksum: test_case.checksum,
|
|
259
|
-
rn_mem: test_case.rn_mem,
|
|
260
|
-
re_mem: test_case.re_mem,
|
|
261
|
-
summary_text: test_case.summary_text
|
|
220
|
+
computer: computer_name,
|
|
221
|
+
platform_version: platform_version
|
|
262
222
|
}
|
|
263
|
-
|
|
264
|
-
# enter in test-specific data, DISABLED FOR NOW
|
|
265
|
-
# test_case.data_names.each do |data_name|
|
|
266
|
-
# unless test_case.data[data_name].nil?
|
|
267
|
-
# res[data_name] = test_case.data[data_name]
|
|
268
|
-
# end
|
|
269
|
-
# end
|
|
270
|
-
res
|
|
271
223
|
end
|
|
272
224
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
225
|
+
# Parameters to be submitted in JSON format for reporting information about
|
|
226
|
+
# the overall commit being tested; used even if only submitting an entire
|
|
227
|
+
# test. This also determines if the submission is for an entire commit
|
|
228
|
+
# (compilation information and every test), an empty commit (just
|
|
229
|
+
# compilation information), or a non-empty, but also non-entire submission
|
|
230
|
+
# (results for a single test without compilation information)
|
|
231
|
+
def commit_params(mesa, entire: true, empty: false)
|
|
232
|
+
# the compiler data should be able to be used as-is, but right now the
|
|
233
|
+
# names don't match with what the database expects, so we do some renaming
|
|
234
|
+
# shenanigans.
|
|
235
|
+
#
|
|
236
|
+
####################################
|
|
237
|
+
# THIS SHOULD GO BEFORE PRODUCTION #
|
|
238
|
+
{
|
|
239
|
+
sha: mesa.sha,
|
|
240
|
+
compiled: mesa.installed?,
|
|
241
|
+
entire: entire,
|
|
242
|
+
empty: empty,
|
|
243
|
+
}.merge(mesa.compiler_hash)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Given a valid +Mesa+ object, create an array of hashes that describe the
|
|
247
|
+
# test cases and the test results. These will be encoded as an array of
|
|
248
|
+
# JSON objects.
|
|
249
|
+
def instance_params(mesa)
|
|
296
250
|
has_errors = []
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
# information that requires the web app to work, stored in :extra
|
|
300
|
-
mesa.test_names.each do |mod, names|
|
|
251
|
+
res = []
|
|
252
|
+
mesa.test_case_names.each do |mod, names|
|
|
301
253
|
names.each do |test_name|
|
|
302
254
|
begin
|
|
303
255
|
test_case = mesa.test_cases[mod][test_name]
|
|
304
|
-
res
|
|
305
|
-
test_instance: {
|
|
306
|
-
runtime_seconds: test_case.runtime_seconds,
|
|
307
|
-
re_time: test_case.re_time,
|
|
308
|
-
total_runtime_seconds: test_case.total_runtime_seconds,
|
|
309
|
-
passed: test_case.passed?,
|
|
310
|
-
compiler: test_case.compiler || compiler,
|
|
311
|
-
compiler_version: test_case.compiler_version || compiler_version,
|
|
312
|
-
platform_version: platform_version,
|
|
313
|
-
omp_num_threads: test_case.test_omp_num_threads,
|
|
314
|
-
success_type: test_case.success_type,
|
|
315
|
-
failure_type: test_case.failure_type,
|
|
316
|
-
steps: test_case.steps,
|
|
317
|
-
retries: test_case.retries,
|
|
318
|
-
backups: test_case.backups,
|
|
319
|
-
diff: test_case.diff,
|
|
320
|
-
checksum: test_case.checksum,
|
|
321
|
-
rn_mem: test_case.rn_mem,
|
|
322
|
-
re_mem: test_case.re_mem,
|
|
323
|
-
summary_text: test_case.summary_text
|
|
324
|
-
},
|
|
325
|
-
extra: { test_case: test_name, mod: mod }
|
|
326
|
-
}
|
|
256
|
+
res << test_case.results_hash
|
|
327
257
|
rescue TestCaseDirError
|
|
328
|
-
shell.say "
|
|
329
|
-
|
|
258
|
+
# shell.say "It appears that #{test_case.test_name} has not been "\
|
|
259
|
+
# 'run yet. Unable to submit data for this test.', :red
|
|
330
260
|
has_errors << test_case
|
|
331
261
|
end
|
|
332
262
|
end
|
|
333
263
|
end
|
|
334
|
-
|
|
264
|
+
unless has_errors.empty?
|
|
265
|
+
shell.say "The following test cases could NOT be read for submission:",
|
|
266
|
+
:red
|
|
267
|
+
has_errors.each do |test_case|
|
|
268
|
+
shell.say "- #{test_case.test_name}", :red
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
res
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Parameters for a single test case. +mesa+ is an instance of +Mesa+, and
|
|
275
|
+
# +test_case+ is an instance of MesaTestCase representing the test case to
|
|
276
|
+
# be submitted
|
|
277
|
+
def single_instance_params(test_case)
|
|
278
|
+
[test_case.results_hash]
|
|
335
279
|
end
|
|
336
280
|
|
|
281
|
+
# Phone home to testhub and confirm that computer and user are valid. Useful
|
|
282
|
+
# for confirming that submissions will be accepted before wasting time on a
|
|
283
|
+
# test later.
|
|
337
284
|
def confirm_computer
|
|
338
285
|
uri = URI.parse(base_uri + '/check_computer.json')
|
|
339
286
|
https = Net::HTTP.new(uri.hostname, uri.port)
|
|
@@ -350,12 +297,10 @@ e-mail and password will be stored in plain text.'
|
|
|
350
297
|
JSON.parse(https.request(request).body).to_hash
|
|
351
298
|
end
|
|
352
299
|
|
|
353
|
-
#
|
|
354
|
-
#
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
def submit(test_case)
|
|
358
|
-
uri = URI.parse(base_uri + '/test_instances/submit.json')
|
|
300
|
+
# submit entire commit's worth of test cases, OR submit compilation status
|
|
301
|
+
# and NO test cases
|
|
302
|
+
def submit_commit(mesa, empty: false)
|
|
303
|
+
uri = URI.parse(base_uri + '/submissions/create.json')
|
|
359
304
|
https = Net::HTTP.new(uri.hostname, uri.port)
|
|
360
305
|
https.use_ssl = true if base_uri.include? 'https'
|
|
361
306
|
|
|
@@ -363,74 +308,38 @@ e-mail and password will be stored in plain text.'
|
|
|
363
308
|
uri,
|
|
364
309
|
initheader = { 'Content-Type' => 'application/json' }
|
|
365
310
|
)
|
|
366
|
-
begin
|
|
367
|
-
request.body = submit_params(test_case).to_json
|
|
368
|
-
rescue TestCaseDirError
|
|
369
|
-
shell.say "\nPassage status for #{test_case.test_name} not yet known. " \
|
|
370
|
-
'Run test first and then submit.', :red
|
|
371
|
-
return false
|
|
372
|
-
end
|
|
373
311
|
|
|
374
|
-
#
|
|
375
|
-
#
|
|
376
|
-
#
|
|
312
|
+
# create the request body for submission to the submissions API
|
|
313
|
+
#
|
|
314
|
+
# if we have an empty submission, then it is necessarily not entire.
|
|
315
|
+
# Similarly, a non-empty submission is necessarily entire (otherwise one
|
|
316
|
+
# would use +submit_instance+)
|
|
317
|
+
request_data = {submitter: submitter_params,
|
|
318
|
+
commit: commit_params(mesa, empty: empty, entire: !empty)}
|
|
319
|
+
# don't need test instances if it's an empty submission or if compilation
|
|
320
|
+
# failed
|
|
321
|
+
if !empty && request_data[:commit][:compiled]
|
|
322
|
+
request_data[:instances] = instance_params(mesa)
|
|
323
|
+
end
|
|
324
|
+
request.body = request_data.to_json
|
|
377
325
|
|
|
326
|
+
# actually do the submission
|
|
378
327
|
response = https.request request
|
|
379
|
-
# puts JSON.parse(response.body).to_hash if verbose
|
|
380
|
-
response.is_a? Net::HTTPCreated
|
|
381
|
-
end
|
|
382
328
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
success = true
|
|
388
|
-
mesa.test_names.each_key do |this_mod|
|
|
389
|
-
success &&= submit_all(mesa, mod = this_mod)
|
|
390
|
-
end
|
|
329
|
+
if !response.is_a? Net::HTTPCreated
|
|
330
|
+
shell.say "\nFailed to submit some or all test case instances and/or "\
|
|
331
|
+
'commit data.', :red
|
|
332
|
+
false
|
|
391
333
|
else
|
|
392
|
-
mesa.
|
|
393
|
-
|
|
394
|
-
test_case = mesa.test_cases[mod][test_name]
|
|
395
|
-
# try to submit and note if it does or doesn't successfully submit
|
|
396
|
-
submitted = false
|
|
397
|
-
submitted = submit(test_case) unless test_case.outcome == :not_tested
|
|
398
|
-
if submitted
|
|
399
|
-
submitted_cases << test_name
|
|
400
|
-
else
|
|
401
|
-
unsubmitted_cases << test_name
|
|
402
|
-
end
|
|
403
|
-
end
|
|
404
|
-
puts "\nSubmission results for #{mod} module:"
|
|
405
|
-
puts '#####################################'
|
|
406
|
-
if !submitted_cases.empty?
|
|
407
|
-
shell.say 'Submitted the following cases:', :green
|
|
408
|
-
puts submitted_cases.join("\n")
|
|
409
|
-
else
|
|
410
|
-
shell.say 'Did not successfully submit any cases.', :red
|
|
411
|
-
end
|
|
412
|
-
unless unsubmitted_cases.empty?
|
|
413
|
-
puts "\n\n\n"
|
|
414
|
-
shell.say 'Failed to submit the following cases:', :red
|
|
415
|
-
puts unsubmitted_cases.join("\n")
|
|
416
|
-
end
|
|
417
|
-
# return true and update last tested if all cases were submitted
|
|
418
|
-
success = submitted_cases.length == mesa.test_names[mod].length
|
|
419
|
-
if success
|
|
420
|
-
@last_tested = mesa.version_number
|
|
421
|
-
shell.say "\n\nUpdating last tested revision to #{last_tested}."
|
|
422
|
-
save_computer_data
|
|
423
|
-
end
|
|
334
|
+
shell.say "\nSuccessfully submitted commit #{mesa.sha}.", :green
|
|
335
|
+
true
|
|
424
336
|
end
|
|
425
|
-
# return boolean indicating whether or not all cases successfully
|
|
426
|
-
# SUBMITTED (irrespective of passing status)
|
|
427
|
-
success
|
|
428
337
|
end
|
|
429
338
|
|
|
430
|
-
#
|
|
431
|
-
#
|
|
432
|
-
def
|
|
433
|
-
uri = URI.parse(base_uri + '/
|
|
339
|
+
# submit results for a single test case instance. Does *not* report overall
|
|
340
|
+
# compilation status to testhub. Use an empty commit submission for that
|
|
341
|
+
def submit_instance(mesa, test_case)
|
|
342
|
+
uri = URI.parse(base_uri + '/submissions/create.json')
|
|
434
343
|
https = Net::HTTP.new(uri.hostname, uri.port)
|
|
435
344
|
https.use_ssl = true if base_uri.include? 'https'
|
|
436
345
|
|
|
@@ -438,207 +347,182 @@ e-mail and password will be stored in plain text.'
|
|
|
438
347
|
uri,
|
|
439
348
|
initheader = { 'Content-Type' => 'application/json' }
|
|
440
349
|
)
|
|
441
|
-
request_data, error_cases = revision_submit_params(mesa)
|
|
442
|
-
if request_data[:instances].empty? && mesa.installed?
|
|
443
|
-
shell.say "No completed test data found in #{mesa.mesa_dir}. Aborting.",
|
|
444
|
-
:red
|
|
445
|
-
return false
|
|
446
|
-
end
|
|
447
|
-
request.body = request_data.to_json
|
|
448
350
|
|
|
449
|
-
#
|
|
450
|
-
#
|
|
451
|
-
#
|
|
351
|
+
# create the request body for submission to the submissions API
|
|
352
|
+
#
|
|
353
|
+
# submission is not empty (there is one test case), and it is also not
|
|
354
|
+
# entire (... there is only test case)
|
|
355
|
+
request_data = {submitter: submitter_params,
|
|
356
|
+
commit: commit_params(mesa, empty: false, entire: false),
|
|
357
|
+
instances: single_instance_params(test_case)}
|
|
358
|
+
request.body = request_data.to_json
|
|
452
359
|
|
|
360
|
+
# actually do the submission
|
|
453
361
|
response = https.request request
|
|
454
|
-
|
|
362
|
+
|
|
455
363
|
if !response.is_a? Net::HTTPCreated
|
|
456
|
-
shell.say "\nFailed to submit
|
|
457
|
-
:red
|
|
458
|
-
false
|
|
459
|
-
elsif !error_cases.empty?
|
|
460
|
-
shell.say "\nFailed to gather data for the following cases:", :red
|
|
461
|
-
error_cases.each { |tc| shell.say " #{tc.test_name}", :red }
|
|
364
|
+
shell.say "\nFailed to submit #{test_case.test_name} for commit "\
|
|
365
|
+
"#{mesa.sha}", :red
|
|
462
366
|
false
|
|
463
367
|
else
|
|
464
|
-
shell.say "\nSuccessfully submitted
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
save_computer_data
|
|
468
|
-
true
|
|
368
|
+
shell.say "\nSuccessfully submitted instance of #{test_case.test_name} "\
|
|
369
|
+
"for commit #{mesa.sha}.", :green
|
|
370
|
+
true
|
|
469
371
|
end
|
|
470
372
|
end
|
|
471
373
|
end
|
|
472
374
|
|
|
473
375
|
class Mesa
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
attr_reader :mesa_dir, :test_data, :test_names, :test_cases, :shell,
|
|
477
|
-
:svn_version, :svn_author, :svn_log, :using_sdk
|
|
478
|
-
attr_accessor :update_checksums
|
|
479
|
-
|
|
480
|
-
def self.download(version_number: nil, new_mesa_dir: nil, use_svn: true,
|
|
481
|
-
using_sdk: true)
|
|
482
|
-
new_mesa_dir ||= File.join(ENV['HOME'], 'mesa-test-r' + version_number.to_s)
|
|
483
|
-
svn_command = "svn co -r #{version_number} #{SVN_URI} #{new_mesa_dir}"
|
|
484
|
-
success = bash_execute(svn_command)
|
|
485
|
-
unless success
|
|
486
|
-
raise MesaDirError, 'Encountered a problem in downloading mesa ' \
|
|
487
|
-
"revision #{version_number}. Perhaps svn isn't " \
|
|
488
|
-
'working properly?' + "\n\n"\
|
|
489
|
-
'Tried the following command: ' + svn_command
|
|
490
|
-
|
|
491
|
-
end
|
|
492
|
-
Mesa.new(mesa_dir: new_mesa_dir, use_svn: use_svn, using_sdk: using_sdk)
|
|
493
|
-
end
|
|
494
|
-
|
|
495
|
-
def self.log_since(last_tested = DEFAULT_REVISION)
|
|
496
|
-
# svn commit log back to, but excluding, the last revision tested
|
|
497
|
-
`svn log #{SVN_URI} -r #{last_tested + 1}:HEAD`
|
|
498
|
-
end
|
|
499
|
-
|
|
500
|
-
def self.log_lines_since(last_tested = DEFAULT_REVISION)
|
|
501
|
-
log_since(last_tested).split("\n").reject(&:empty?)
|
|
502
|
-
end
|
|
503
|
-
|
|
504
|
-
def self.add_commit(commits, revision, author)
|
|
505
|
-
commits << Commit.new
|
|
506
|
-
commits.last.revision = revision.to_i
|
|
507
|
-
commits.last.author = author
|
|
508
|
-
commits.last.message = []
|
|
509
|
-
end
|
|
510
|
-
|
|
511
|
-
def self.process_line(commits, line)
|
|
512
|
-
last = commits.last
|
|
513
|
-
if line =~ /^-+$/
|
|
514
|
-
# dashed lines separate commits
|
|
515
|
-
# Done with last commit (if it exists), so clean up message
|
|
516
|
-
last.message = last.message.join("\n") unless last.nil?
|
|
517
|
-
elsif line =~ /^r(\d+) \| (\w+) \| .* \| \d+ lines?$/
|
|
518
|
-
# first line of a commit, scrape data and make new commit
|
|
519
|
-
add_commit(commits, $1, $2)
|
|
520
|
-
else
|
|
521
|
-
# add lines to the message (will concatenate later to single String)
|
|
522
|
-
last.message << line.strip
|
|
523
|
-
end
|
|
524
|
-
end
|
|
525
|
-
|
|
526
|
-
# all commits since the given version number
|
|
527
|
-
def self.commits_since(last_tested = DEFAULT_REVISION)
|
|
528
|
-
commits = []
|
|
529
|
-
log_lines_since(last_tested).each { |line| process_line(commits, line) }
|
|
530
|
-
commits.sort_by(&:revision).reverse
|
|
531
|
-
end
|
|
376
|
+
attr_reader :mesa_dir, :mirror_dir, :names_to_numbers, :shell,
|
|
377
|
+
:test_case_names, :test_cases, :github_protocol
|
|
532
378
|
|
|
533
|
-
def self.
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
379
|
+
def self.checkout(sha: nil, work_dir: nil, mirror_dir: nil,
|
|
380
|
+
github_protocol: :ssh)
|
|
381
|
+
m = Mesa.new(mesa_dir: work_dir, mirror_dir: mirror_dir,
|
|
382
|
+
github_protocol: github_protocol)
|
|
383
|
+
m.checkout(new_sha: sha)
|
|
384
|
+
m
|
|
539
385
|
end
|
|
540
386
|
|
|
541
|
-
def initialize(mesa_dir: ENV['MESA_DIR'],
|
|
387
|
+
def initialize(mesa_dir: ENV['MESA_DIR'], mirror_dir: nil,
|
|
388
|
+
github_protocol: :ssh)
|
|
542
389
|
# absolute_path ensures that it doesn't matter where commands are executed
|
|
543
390
|
# from
|
|
544
391
|
@mesa_dir = File.absolute_path(mesa_dir)
|
|
545
|
-
@
|
|
546
|
-
|
|
547
|
-
|
|
392
|
+
@mirror_dir = File.absolute_path(mirror_dir)
|
|
393
|
+
|
|
394
|
+
# don't worry about validity of github protocol until it is needed in a
|
|
395
|
+
# checkout. This way you can have garbage in there if you never really need
|
|
396
|
+
# it.
|
|
397
|
+
@github_protocol = if github_protocol.respond_to? :to_sym
|
|
398
|
+
github_protocol.to_sym
|
|
399
|
+
else
|
|
400
|
+
github_protocol
|
|
401
|
+
end
|
|
548
402
|
|
|
549
403
|
# these get populated by calling #load_test_data
|
|
550
|
-
@test_data = {}
|
|
551
|
-
@test_names = {}
|
|
552
404
|
@test_cases = {}
|
|
405
|
+
@test_case_names = {}
|
|
406
|
+
@names_to_numbers = {}
|
|
553
407
|
|
|
554
408
|
# way to output colored text
|
|
555
409
|
@shell = Thor::Shell::Color.new
|
|
556
|
-
|
|
557
|
-
# these can be populated by calling load_svn_data
|
|
558
|
-
@svn_version = nil
|
|
559
|
-
@svn_author = nil
|
|
560
|
-
@svn_log = nil
|
|
561
|
-
load_svn_data if use_svn?
|
|
562
|
-
end
|
|
563
|
-
|
|
564
|
-
def use_svn?
|
|
565
|
-
@use_svn
|
|
566
410
|
end
|
|
567
411
|
|
|
568
|
-
def
|
|
569
|
-
#
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
return unless @svn_log
|
|
575
|
-
|
|
576
|
-
# by default, DON'T do diffs
|
|
577
|
-
@update_checksums = true
|
|
578
|
-
|
|
579
|
-
# list of phrases, which, if present in the log entry, will trigger diffs
|
|
580
|
-
[
|
|
581
|
-
/updated? checksums?/i,
|
|
582
|
-
/checksums? updated?/i,
|
|
583
|
-
/ready for diffs?/i,
|
|
584
|
-
].each { |trigger| @update_checksums = false if trigger =~ @svn_log }
|
|
585
|
-
if @update_checksums
|
|
586
|
-
shell.say "\nFrom svn log, didn't decide to tak diffs."
|
|
412
|
+
def checkout(new_sha: 'HEAD')
|
|
413
|
+
# before anything confirm that git-lfs has been installed
|
|
414
|
+
shell.say "\nEnsuring that git-lfs is installed... ", :blue
|
|
415
|
+
command = 'git lfs help >> /dev/null 2>&1'
|
|
416
|
+
if bash_execute(command)
|
|
417
|
+
shell.say "yes", :green
|
|
587
418
|
else
|
|
588
|
-
shell.say "
|
|
419
|
+
shell.say "no", :red
|
|
420
|
+
raise(GitHubError, "The command #{command} returned with an error "\
|
|
421
|
+
'status, indicating that git-lfs is not installed. '\
|
|
422
|
+
'Make sure it is installed and try again.')
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# set up mirror if it doesn't exist
|
|
426
|
+
unless dir_or_symlink_exists?(mirror_dir)
|
|
427
|
+
shell.say "\nCreating initial mirror at #{mirror_dir}. "\
|
|
428
|
+
'This might take awhile...', :blue
|
|
429
|
+
FileUtils.mkdir_p mirror_dir
|
|
430
|
+
case github_protocol
|
|
431
|
+
when :ssh
|
|
432
|
+
command = "git clone --mirror #{GITHUB_SSH} #{mirror_dir}"
|
|
433
|
+
shell.say command
|
|
434
|
+
# fail loudly if this doesn't work
|
|
435
|
+
unless bash_execute(command)
|
|
436
|
+
# nuke the mirror directory since it is probably bogus (make this
|
|
437
|
+
# code fire off the next time checkout is done)
|
|
438
|
+
shell.say "Failed. Removing the [potentially corrupted] mirror.", :red
|
|
439
|
+
command = "rm -rf #{mirror_dir}"
|
|
440
|
+
shell.say command
|
|
441
|
+
bash_execute(command)
|
|
442
|
+
|
|
443
|
+
raise(GitHubError, 'Error while executing the following command:'\
|
|
444
|
+
"#{command}. Perhaps you haven't set up "\
|
|
445
|
+
'ssh keys with your GitHub account?')
|
|
446
|
+
end
|
|
447
|
+
when :https
|
|
448
|
+
command = "git clone --mirror #{GITHUB_HTTPS} #{mirror_dir}"
|
|
449
|
+
shell.say command
|
|
450
|
+
# fail loudly if this doesn't work
|
|
451
|
+
unless bash_execute(command)
|
|
452
|
+
# nuke the mirror directory since it is probably bogus (make this
|
|
453
|
+
# code fire off the next time checkout is done)
|
|
454
|
+
shell.say "Failed. Removing the [potentially corrupted] mirror.", :red
|
|
455
|
+
command = "rm -rf #{mirror_dir}"
|
|
456
|
+
shell.say command
|
|
457
|
+
bash_execute(command)
|
|
458
|
+
|
|
459
|
+
raise(GitHubError, 'Error while executing the following command: '\
|
|
460
|
+
"#{command}. Perhaps you need to configure "\
|
|
461
|
+
'global GitHub account settings for https '\
|
|
462
|
+
'authentication to work properly?')
|
|
463
|
+
end
|
|
464
|
+
else
|
|
465
|
+
raise(GitHubError, "Invalid GitHub protocol: \"#{github_protocol}\"")
|
|
466
|
+
end
|
|
589
467
|
end
|
|
590
|
-
shell.say "log entry: #{@svn_log}"
|
|
591
|
-
end
|
|
592
468
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
#
|
|
596
|
-
|
|
597
|
-
|
|
469
|
+
update_mirror
|
|
470
|
+
|
|
471
|
+
# ensure "work" directory is removed from worktree
|
|
472
|
+
remove
|
|
473
|
+
|
|
474
|
+
# create "work" directory with proper commit
|
|
475
|
+
shell.say "\nSetting up worktree repo...", :blue
|
|
476
|
+
FileUtils.mkdir_p mesa_dir
|
|
477
|
+
command = "git -C #{mirror_dir} worktree add #{mesa_dir} #{new_sha}"
|
|
478
|
+
shell.say command
|
|
479
|
+
return if bash_execute(command)
|
|
480
|
+
|
|
481
|
+
raise(GitHubError, 'Failed while executing the following command: '\
|
|
482
|
+
"\"#{command}\".")
|
|
598
483
|
end
|
|
599
484
|
|
|
600
|
-
def
|
|
601
|
-
|
|
485
|
+
def update_mirror
|
|
486
|
+
shell.say "\nFetching MESA history...", :blue
|
|
487
|
+
command = "git -C #{mirror_dir} fetch origin"
|
|
488
|
+
shell.say command
|
|
489
|
+
# fail loudly
|
|
490
|
+
return if bash_execute(command)
|
|
491
|
+
|
|
492
|
+
raise(GitHubError, 'Failed while executing the following command: '\
|
|
493
|
+
"\"#{command}\".")
|
|
602
494
|
end
|
|
603
495
|
|
|
604
|
-
def
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
496
|
+
def remove
|
|
497
|
+
return unless File.exist? mesa_dir
|
|
498
|
+
shell.say "\nRemoving work directory from worktree (clearing old data)...",
|
|
499
|
+
:blue
|
|
500
|
+
command = "git -C #{mirror_dir} worktree remove --force #{mesa_dir}"
|
|
501
|
+
shell.say command
|
|
502
|
+
return if bash_execute(command)
|
|
503
|
+
|
|
504
|
+
shell.say "Failed. Simply trying to remove the directory.", :red
|
|
505
|
+
command = "rm -rf #{mesa_dir}"
|
|
506
|
+
shell.say command
|
|
507
|
+
# fail loudly (the "true" tells bash_execute to raise an exception if
|
|
508
|
+
# the command fails)
|
|
509
|
+
bash_execute(command, true)
|
|
612
510
|
end
|
|
613
511
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
# match output of svn info to a line with the revision, capturing the
|
|
617
|
-
# number, and defaulting to 0 if none is found.
|
|
618
|
-
matches = /Revision\:\s+(\d+)/.match(`svn info #{mesa_dir}`)
|
|
619
|
-
unless matches.nil?
|
|
620
|
-
return matches[1].to_i
|
|
621
|
-
end
|
|
622
|
-
return 0
|
|
623
|
-
rescue Errno::ENOENT
|
|
624
|
-
return 0
|
|
512
|
+
def git_sha
|
|
513
|
+
bashticks("git -C #{mesa_dir} rev-parse HEAD")
|
|
625
514
|
end
|
|
626
515
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
contents = ''
|
|
630
|
-
File.open(File.join(mesa_dir, 'data', 'version_number'), 'r') do |f|
|
|
631
|
-
contents = f.read
|
|
632
|
-
end
|
|
633
|
-
contents.strip.to_i
|
|
516
|
+
def sha
|
|
517
|
+
git_sha
|
|
634
518
|
end
|
|
635
519
|
|
|
636
520
|
def clean
|
|
637
521
|
with_mesa_dir do
|
|
638
522
|
visit_and_check mesa_dir, MesaDirError, 'E\countered a problem in ' \
|
|
639
523
|
"running `clean` in #{mesa_dir}." do
|
|
640
|
-
|
|
641
|
-
|
|
524
|
+
shell.say('MESA_DIR = ' + ENV['MESA_DIR'])
|
|
525
|
+
shell.say './clean'
|
|
642
526
|
bash_execute('./clean')
|
|
643
527
|
end
|
|
644
528
|
end
|
|
@@ -649,8 +533,8 @@ class Mesa
|
|
|
649
533
|
with_mesa_dir do
|
|
650
534
|
visit_and_check mesa_dir, MesaDirError, 'Encountered a problem in ' \
|
|
651
535
|
"running `install` in #{mesa_dir}." do
|
|
652
|
-
|
|
653
|
-
|
|
536
|
+
shell.say('MESA_DIR = ' + ENV['MESA_DIR'])
|
|
537
|
+
shell.say './install'
|
|
654
538
|
bash_execute('./install')
|
|
655
539
|
end
|
|
656
540
|
end
|
|
@@ -661,20 +545,31 @@ class Mesa
|
|
|
661
545
|
|
|
662
546
|
# throw an error unless it seems like it's properly compiled
|
|
663
547
|
def check_installation
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
548
|
+
return if installed?
|
|
549
|
+
|
|
550
|
+
raise MesaDirError, 'Installation check failed (build.log doesn\'t '\
|
|
551
|
+
'show a successful installation).'
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
# sourced from $MESA_DIR/testhub.yml, which should be created after
|
|
555
|
+
# installation
|
|
556
|
+
def compiler_hash
|
|
557
|
+
data_file = File.join(mesa_dir, 'testhub.yml')
|
|
558
|
+
unless File.exist? data_file
|
|
559
|
+
raise(MesaDirError, "Could not find file testhub.yml in #{mesa_dir}.")
|
|
667
560
|
end
|
|
668
|
-
end
|
|
669
561
|
|
|
670
|
-
|
|
671
|
-
|
|
562
|
+
res = YAML.safe_load(File.read(data_file))
|
|
563
|
+
# currently version_number is reported, but we don't need that in Git land
|
|
564
|
+
res.delete('version_number') # returns the value, not the updated hash
|
|
565
|
+
res
|
|
672
566
|
end
|
|
673
567
|
|
|
674
568
|
## TEST SUITE METHODS
|
|
675
569
|
|
|
676
570
|
def check_mod(mod)
|
|
677
571
|
return if MesaTestCase.modules.include? mod
|
|
572
|
+
|
|
678
573
|
raise TestCaseDirError, "Invalid module: #{mod}. Must be one of: " +
|
|
679
574
|
MesaTestCase.modules.join(', ')
|
|
680
575
|
end
|
|
@@ -694,53 +589,32 @@ class Mesa
|
|
|
694
589
|
end
|
|
695
590
|
else
|
|
696
591
|
check_mod mod
|
|
697
|
-
# load data from the source file
|
|
698
|
-
source_lines = IO.readlines(
|
|
699
|
-
File.join(test_suite_dir(mod: mod), 'do1_test_source')
|
|
700
|
-
)
|
|
701
592
|
|
|
702
|
-
#
|
|
703
|
-
|
|
704
|
-
@
|
|
593
|
+
# convert output of +list_tests+ to a dictionary that maps
|
|
594
|
+
# names to numbers since +each_test_run+ only knows about numbers
|
|
595
|
+
@names_to_numbers[mod] = {}
|
|
596
|
+
@test_case_names[mod] = []
|
|
705
597
|
@test_cases[mod] = {}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
photo: $4}
|
|
718
|
-
elsif line =~ one_skip
|
|
719
|
-
found_test = true
|
|
720
|
-
@test_data[mod][$1] = { success_string: $2, final_model: $3,
|
|
721
|
-
photo: nil }
|
|
722
|
-
elsif line =~ two_skip
|
|
723
|
-
found_test = true
|
|
724
|
-
@test_data[mod][$1] = { success_string: $2, final_model: nil,
|
|
725
|
-
photo: nil }
|
|
726
|
-
end
|
|
727
|
-
|
|
728
|
-
if found_test
|
|
729
|
-
@test_names[mod] << $1 unless @test_names[mod].include? $1
|
|
598
|
+
visit_dir(test_suite_dir(mod: mod), quiet: true) do
|
|
599
|
+
bashticks('./list_tests').split("\n").each do |line|
|
|
600
|
+
num, tc_name = line.strip.split
|
|
601
|
+
@names_to_numbers[mod][tc_name.strip] = num.to_i
|
|
602
|
+
@test_case_names[mod] << tc_name.strip
|
|
603
|
+
@test_cases[mod][tc_name.strip] = MesaTestCase.new(
|
|
604
|
+
test: tc_name.strip,
|
|
605
|
+
mod: mod,
|
|
606
|
+
position: num.to_i,
|
|
607
|
+
mesa: self
|
|
608
|
+
)
|
|
730
609
|
end
|
|
731
610
|
end
|
|
732
|
-
|
|
733
|
-
# make MesaTestCase objects accessible by name
|
|
734
|
-
@test_names[mod].each do |test_name|
|
|
735
|
-
data = @test_data[mod][test_name]
|
|
736
|
-
@test_cases[mod][test_name] = MesaTestCase.new(
|
|
737
|
-
test: test_name, mesa: self, success_string: data[:success_string],
|
|
738
|
-
mod: mod, final_model: data[:final_model], photo: data[:photo]
|
|
739
|
-
)
|
|
740
|
-
end
|
|
741
611
|
end
|
|
742
612
|
end
|
|
743
613
|
|
|
614
|
+
def test_case_count(mod: :all)
|
|
615
|
+
all_names_ordered(mod: mod).count
|
|
616
|
+
end
|
|
617
|
+
|
|
744
618
|
# can accept a number (in string form) as a name for indexed access
|
|
745
619
|
def find_test_case(test_case_name: nil, mod: :all)
|
|
746
620
|
if /\A[0-9]+\z/ =~ test_case_name
|
|
@@ -750,44 +624,16 @@ class Mesa
|
|
|
750
624
|
end
|
|
751
625
|
end
|
|
752
626
|
|
|
753
|
-
|
|
754
|
-
# revision 10000
|
|
755
|
-
def each_test_clean(mod: :all)
|
|
756
|
-
if mod == :all
|
|
757
|
-
MesaTestCase.modules.each { |this_mod| each_test_clean mod: this_mod }
|
|
758
|
-
else
|
|
759
|
-
check_mod mod
|
|
760
|
-
test_names[mod].each do |test_name|
|
|
761
|
-
test_cases[mod][test_name].clean
|
|
762
|
-
end
|
|
763
|
-
end
|
|
764
|
-
end
|
|
765
|
-
|
|
766
|
-
def each_test_run_and_diff(mod: :all, log_results: false)
|
|
627
|
+
def each_test_run(mod: :all)
|
|
767
628
|
check_installation
|
|
768
|
-
each_test_clean(mod: mod)
|
|
769
|
-
|
|
770
|
-
if mod == :all
|
|
771
|
-
MesaTestCase.modules.each do |this_mod|
|
|
772
|
-
each_test_run_and_diff(mod: this_mod, log_results: log_results)
|
|
773
|
-
end
|
|
774
|
-
else
|
|
775
|
-
test_names[mod].each do |test_name|
|
|
776
|
-
test_cases[mod][test_name].do_one
|
|
777
|
-
test_cases[mod][test_name].log_results if log_results
|
|
778
|
-
end
|
|
779
|
-
log_summary(mod: mod) if log_results
|
|
780
|
-
end
|
|
781
|
-
end
|
|
782
629
|
|
|
783
|
-
def each_test_load_results(mod: :all)
|
|
784
630
|
if mod == :all
|
|
785
631
|
MesaTestCase.modules.each do |this_mod|
|
|
786
|
-
|
|
632
|
+
each_test_run(mod: this_mod)
|
|
787
633
|
end
|
|
788
634
|
else
|
|
789
|
-
|
|
790
|
-
|
|
635
|
+
visit_dir(test_suite_dir(mod: mod)) do
|
|
636
|
+
bash_execute('./each_test_run')
|
|
791
637
|
end
|
|
792
638
|
end
|
|
793
639
|
end
|
|
@@ -797,34 +643,21 @@ class Mesa
|
|
|
797
643
|
end
|
|
798
644
|
|
|
799
645
|
def installed?
|
|
800
|
-
#
|
|
801
|
-
#
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
# SOME_MODULE", after which the "SOME_MODULE" will be stored in $1
|
|
806
|
-
# that is the last module to be compiled by ./install.
|
|
807
|
-
IO.readlines(install_file).select do |line|
|
|
808
|
-
line =~ /^\s*do_one\w*\s+\w+/
|
|
809
|
-
end.last =~ /^\s*do_one\w*\s+(\w+)/
|
|
810
|
-
# module is "installed" if there is a nonzero number of files in the
|
|
811
|
-
# module's make directory of the form SOMETHING.mod
|
|
812
|
-
!Dir.entries(File.join(mesa_dir, $1, 'make')).select do |file|
|
|
813
|
-
File.extname(file) == '.mod'
|
|
814
|
-
end.empty?
|
|
646
|
+
# assume build log reflects installation status; does not account for
|
|
647
|
+
# mucking with modules after the fact
|
|
648
|
+
downloaded? && File.read(File.join(mesa_dir, 'build.log')).include?(
|
|
649
|
+
'MESA installation was successful'
|
|
650
|
+
)
|
|
815
651
|
end
|
|
816
652
|
|
|
817
|
-
|
|
818
653
|
private
|
|
819
654
|
|
|
820
|
-
# verify that mesa_dir is valid by checking for
|
|
821
|
-
# directory
|
|
655
|
+
# verify that mesa_dir is valid by checking for existence of test_suite
|
|
656
|
+
# directory for each module (somewhat arbitrary)
|
|
822
657
|
def check_mesa_dir
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
res &&= File.directory?(test_suite_dir(mod: mod))
|
|
658
|
+
MesaTestCase.modules.inject(true) do |res, mod|
|
|
659
|
+
res && dir_or_symlink_exists?(test_suite_dir(mod: mod))
|
|
826
660
|
end
|
|
827
|
-
res
|
|
828
661
|
end
|
|
829
662
|
|
|
830
663
|
# change MESA_DIR for the execution of the block and then revert to the
|
|
@@ -845,49 +678,64 @@ class Mesa
|
|
|
845
678
|
end
|
|
846
679
|
end
|
|
847
680
|
|
|
848
|
-
def
|
|
681
|
+
def all_names_ordered(mod: :all)
|
|
682
|
+
load_test_source_data unless @names_to_numbers
|
|
849
683
|
if mod == :all
|
|
850
|
-
|
|
851
|
-
|
|
684
|
+
# build up list by first constructing each modules list and then
|
|
685
|
+
# concatenating them
|
|
686
|
+
MesaTestCase.modules.inject([]) do |res, this_mod|
|
|
687
|
+
res += all_names_ordered(mod: this_mod)
|
|
852
688
|
end
|
|
853
689
|
else
|
|
854
690
|
check_mod mod
|
|
855
|
-
res = []
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
'failure_type' => test_case.failure_type,
|
|
862
|
-
'success_type' => test_case.success_type,
|
|
863
|
-
'runtime_seconds' => test_case.runtime_seconds,
|
|
864
|
-
'omp_num_threads' => test_case.test_omp_num_threads,
|
|
865
|
-
'mesa_version' => test_case.mesa_version
|
|
866
|
-
}
|
|
867
|
-
end
|
|
868
|
-
summary_file = File.join(test_suite_dir(mod: mod), 'test_summary.yml')
|
|
869
|
-
File.open(summary_file, 'w') do |f|
|
|
870
|
-
f.write(YAML.dump(res))
|
|
691
|
+
res = Array.new(@names_to_numbers[mod].length, '')
|
|
692
|
+
|
|
693
|
+
# values of the hash give their order, keys are the names, so
|
|
694
|
+
# we assign keys to positions in the array according to their value
|
|
695
|
+
@names_to_numbers[mod].each_pair do |key, val|
|
|
696
|
+
res[val - 1] = key # +list_tests+ gives 1-indexed positions
|
|
871
697
|
end
|
|
698
|
+
res
|
|
872
699
|
end
|
|
873
700
|
end
|
|
874
701
|
|
|
875
702
|
def find_test_case_by_name(test_case_name: nil, mod: :all)
|
|
703
|
+
load_test_source_data unless @names_to_numbers
|
|
876
704
|
if mod == :all
|
|
877
|
-
# look through all loaded modules for desired test case name,
|
|
878
|
-
#
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
705
|
+
# look through all loaded modules for desired test case name, only
|
|
706
|
+
# return a test case if a single case is found with that name
|
|
707
|
+
case all_names_ordered.count(test_case_name)
|
|
708
|
+
when 1
|
|
709
|
+
# it exists in exactly one module, but we need to find the module
|
|
710
|
+
# and then return the +MesaTestCase+ object
|
|
711
|
+
MesaTestCase.modules.each do |this_mod|
|
|
712
|
+
if @test_case_names[this_mod].include? test_case_name
|
|
713
|
+
# found it, return the appropriate object
|
|
714
|
+
return @test_cases[this_mod][test_case_name]
|
|
715
|
+
end
|
|
882
716
|
end
|
|
717
|
+
raise 'Weird problem: found test case in overall names, but '\
|
|
718
|
+
"not in any particular module. This shouldn't happen."
|
|
719
|
+
when 0
|
|
720
|
+
raise(TestCaseDirError, "Could not find test case #{test_case_name} "\
|
|
721
|
+
'in any module.')
|
|
722
|
+
else
|
|
723
|
+
raise(TestCaseDirError, 'Found multiple test cases named '\
|
|
724
|
+
"#{test_case_name} in multiple modules. Indicate the module you "\
|
|
725
|
+
'want to search.')
|
|
883
726
|
end
|
|
884
|
-
#
|
|
885
|
-
nil
|
|
727
|
+
# append this array to the end of the exisitng one
|
|
886
728
|
else
|
|
887
729
|
# module specified; check it and return the proper test case (may be nil
|
|
888
730
|
# if the test case doesn't exist)
|
|
889
731
|
check_mod mod
|
|
890
|
-
@
|
|
732
|
+
if @test_case_names[mod].include? test_case_name
|
|
733
|
+
# happy path: test case exists in the specified module
|
|
734
|
+
return @test_cases[mod][test_case_name]
|
|
735
|
+
else
|
|
736
|
+
raise TestCaseDirError.new('Could not find test case ' \
|
|
737
|
+
"#{test_case_name} in the #{mod} module.")
|
|
738
|
+
end
|
|
891
739
|
end
|
|
892
740
|
end
|
|
893
741
|
|
|
@@ -895,149 +743,69 @@ class Mesa
|
|
|
895
743
|
# this will be the index in the name array of the proper module of
|
|
896
744
|
# the desired test case
|
|
897
745
|
# input numbers are 1-indexed, but we'll fix that later
|
|
898
|
-
|
|
899
|
-
|
|
746
|
+
if test_number < 1 || test_number > test_case_count(mod: mod)
|
|
747
|
+
raise TestCaseDirError.new('Invalid test case number for searching '\
|
|
748
|
+
"in module #{mod}. Must be between 1 and #{test_case_count(mod: mod)}.")
|
|
749
|
+
end
|
|
900
750
|
|
|
901
751
|
if mod == :all
|
|
902
|
-
#
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
test_case_name: @test_names[this_mod][i - 1],
|
|
913
|
-
mod: this_mod
|
|
752
|
+
# can get the name easily, now need to find the module
|
|
753
|
+
test_case_name = all_names_ordered[test_number - 1]
|
|
754
|
+
MesaTestCase.modules.each do |mod|
|
|
755
|
+
if test_number <= test_case_count(mod: mod)
|
|
756
|
+
# test must live in this module; we have everything
|
|
757
|
+
return MesaTestCase.new(
|
|
758
|
+
test: test_case_name,
|
|
759
|
+
mod: mod,
|
|
760
|
+
mesa: self,
|
|
761
|
+
position: @names_to_numbers[mod][test_case_name]
|
|
914
762
|
)
|
|
763
|
+
else
|
|
764
|
+
# number was too big, so decrement by this modules case count
|
|
765
|
+
# and move on to next one
|
|
766
|
+
test_number -= test_case_count(mod: mod)
|
|
915
767
|
end
|
|
916
|
-
# index lies outside possible range for this module, move on to
|
|
917
|
-
# next module and decrement index by the number of test cases in this
|
|
918
|
-
# module
|
|
919
|
-
i -= @test_names[this_mod].length
|
|
920
768
|
end
|
|
921
|
-
# return
|
|
922
|
-
|
|
769
|
+
# should return before we get here, but fail hard if we do
|
|
770
|
+
raise TestCaseDirError.new('Unknown problem in loading test case #' +
|
|
771
|
+
test_number + '.')
|
|
923
772
|
else
|
|
924
|
-
# module was specified, so
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
773
|
+
# module was specified, so we can get at everything right away
|
|
774
|
+
check_mod mod
|
|
775
|
+
return MesaTestCase.new(
|
|
776
|
+
test: all_names_ordered(mod: mod)[test_number - 1],
|
|
777
|
+
mod: mod,
|
|
778
|
+
mesa: self,
|
|
779
|
+
position: test_number
|
|
931
780
|
)
|
|
932
781
|
end
|
|
933
782
|
end
|
|
934
783
|
end
|
|
935
784
|
|
|
936
785
|
class MesaTestCase
|
|
937
|
-
attr_reader :test_name, :
|
|
938
|
-
:failure_msg, :success_msg, :photo, :runtime_seconds,
|
|
939
|
-
:test_omp_num_threads, :mesa_version, :shell, :mod, :retries,
|
|
940
|
-
:backups, :steps, :runtime_minutes, :summary_text, :compiler,
|
|
941
|
-
:compiler_version, :diff, :checksum, :rn_mem, :re_mem,
|
|
942
|
-
:re_time, :total_runtime_seconds
|
|
943
|
-
attr_accessor :data_names, :data_types, :failure_type, :success_type,
|
|
944
|
-
:outcome
|
|
786
|
+
attr_reader :test_name, :mesa, :mod, :position, :shell
|
|
945
787
|
|
|
946
788
|
def self.modules
|
|
947
|
-
%i[star binary]
|
|
789
|
+
%i[star binary astero]
|
|
948
790
|
end
|
|
949
791
|
|
|
950
|
-
def initialize(test: nil, mesa: nil,
|
|
951
|
-
final_model: 'final.mod', photo: nil, mod: nil)
|
|
792
|
+
def initialize(test: nil, mesa: nil, mod: nil, position: nil)
|
|
952
793
|
@test_name = test
|
|
953
|
-
@mesa_dir = mesa.mesa_dir
|
|
954
794
|
@mesa = mesa
|
|
955
|
-
@mesa_version = mesa.version_number
|
|
956
|
-
@success_string = success_string
|
|
957
|
-
@final_model = final_model
|
|
958
|
-
@photo = photo
|
|
959
|
-
@failure_type = nil
|
|
960
|
-
@success_type = nil
|
|
961
|
-
@outcome = :not_tested
|
|
962
|
-
@runtime_seconds = 0
|
|
963
|
-
@test_omp_num_threads = 1
|
|
964
|
-
@runtime_minutes = 0
|
|
965
|
-
@total_runtime_seconds = 0
|
|
966
|
-
@retries = 0
|
|
967
|
-
@backups = 0
|
|
968
|
-
@steps = 0
|
|
969
|
-
# 2 (default) means uknown. Updated by running or loading data.
|
|
970
|
-
# 1 means did diffs (not update_checksums; like each_test_run_and_diff)
|
|
971
|
-
# 0 means no diffs (update_checksums; like each_test_run)
|
|
972
|
-
@diff = 2
|
|
973
|
-
# start with nil. Should only be updated to a non-nil value if test is
|
|
974
|
-
# completely successful
|
|
975
|
-
@checksum = nil
|
|
976
|
-
@re_time = nil # rn_time is in the form of @runtime_seconds
|
|
977
|
-
|
|
978
|
-
# these only get used with modern versions of both the sdk and the test
|
|
979
|
-
# suite
|
|
980
|
-
@rn_mem = nil
|
|
981
|
-
@re_mem = nil
|
|
982
|
-
|
|
983
|
-
# note: this gets overridden for new runs, so this is probably irrelevant
|
|
984
|
-
@summary_text = nil
|
|
985
|
-
|
|
986
|
-
# this overrides the submitters choice if it is non-nil
|
|
987
|
-
@compiler = mesa.using_sdk ? 'SDK' : nil
|
|
988
|
-
# only relevant if @compiler is SDK. Gets set during do_one
|
|
989
|
-
@compiler_version = nil
|
|
990
|
-
|
|
991
795
|
unless MesaTestCase.modules.include? mod
|
|
992
796
|
raise TestCaseDirError, "Invalid module: #{mod}. Must be one of: " +
|
|
993
797
|
MesaTestCase.modules.join(', ')
|
|
994
798
|
end
|
|
995
799
|
@mod = mod
|
|
996
|
-
@
|
|
997
|
-
run_test_string: "#{test_name} run failed: does not match test string",
|
|
998
|
-
final_model: "#{test_name} run failed: final model #{final_model} not " \
|
|
999
|
-
'made.',
|
|
1000
|
-
run_checksum: "#{test_name} run failed: checksum for #{final_model} " \
|
|
1001
|
-
'does not match after ./rn',
|
|
1002
|
-
run_diff: "#{test_name} run failed: diff #{final_model} " \
|
|
1003
|
-
'final_check.mod after ./rn',
|
|
1004
|
-
photo_file: "#{test_name} restart failed: #{photo} does not exist",
|
|
1005
|
-
photo_checksum: "#{test_name} restart failed: checksum for " \
|
|
1006
|
-
"#{final_model} does not match after ./re",
|
|
1007
|
-
photo_diff: "#{test_name} restart failed: diff #{final_model} " \
|
|
1008
|
-
'final_check.mod after ./re',
|
|
1009
|
-
compilation: "#{test_name} compilation failed"
|
|
800
|
+
@position = position
|
|
1010
801
|
|
|
1011
|
-
|
|
1012
|
-
@
|
|
1013
|
-
run_test_string: "#{test_name} run: found test string: " \
|
|
1014
|
-
"'#{success_string}'",
|
|
1015
|
-
run_checksum: "#{test_name} run: checksum for #{final_model} matches " \
|
|
1016
|
-
'after ./rn',
|
|
1017
|
-
photo_checksum: "#{test_name} restart: checksum for #{final_model} " \
|
|
1018
|
-
"matches after ./re #{photo}"
|
|
1019
|
-
}
|
|
802
|
+
# way to output colored text to shell
|
|
803
|
+
@shell = Thor::Shell::Color.new
|
|
1020
804
|
|
|
1021
805
|
# validate stuff
|
|
1022
806
|
check_mesa_dir
|
|
1023
807
|
check_test_case
|
|
1024
808
|
|
|
1025
|
-
@data = {}
|
|
1026
|
-
@data_names = []
|
|
1027
|
-
|
|
1028
|
-
# way to output colored text to shell
|
|
1029
|
-
@shell = Thor::Shell::Color.new
|
|
1030
|
-
end
|
|
1031
|
-
|
|
1032
|
-
def passed?
|
|
1033
|
-
if @outcome == :pass
|
|
1034
|
-
true
|
|
1035
|
-
elsif @outcome == :fail
|
|
1036
|
-
false
|
|
1037
|
-
else
|
|
1038
|
-
raise TestCaseDirError, 'Cannot determine pass/fail status of ' \
|
|
1039
|
-
"#{test_name} yet."
|
|
1040
|
-
end
|
|
1041
809
|
end
|
|
1042
810
|
|
|
1043
811
|
def test_suite_dir
|
|
@@ -1048,214 +816,26 @@ class MesaTestCase
|
|
|
1048
816
|
File.join(test_suite_dir, test_name)
|
|
1049
817
|
end
|
|
1050
818
|
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
raise InvalidDataType, "Invalid data type: #{datum_type}. Must be one "\
|
|
1054
|
-
'of ' + data_types.join(', ') + '.'
|
|
1055
|
-
end
|
|
1056
|
-
@data[datum_name] = datum_type
|
|
1057
|
-
@data_names << datum_name
|
|
1058
|
-
end
|
|
1059
|
-
|
|
1060
|
-
def omp_num_threads
|
|
1061
|
-
ENV['OMP_NUM_THREADS'].to_i || 1
|
|
1062
|
-
end
|
|
1063
|
-
|
|
1064
|
-
# based on $MESA_DIR/star/test_suite/each_test_clean, revision 10000
|
|
1065
|
-
def clean
|
|
1066
|
-
shell.say("Cleaning #{test_name}", color = :yellow)
|
|
1067
|
-
# puts ''
|
|
1068
|
-
check_mesa_dir
|
|
1069
|
-
check_test_case
|
|
1070
|
-
in_dir do
|
|
1071
|
-
puts './clean'
|
|
1072
|
-
unless bash_execute('./clean')
|
|
1073
|
-
raise TestCaseDirError, 'Encountered an error while running ./clean ' \
|
|
1074
|
-
"in #{Dir.getwd}."
|
|
1075
|
-
end
|
|
1076
|
-
shell.say 'Removing all files from LOGS, LOGS1, LOGS2, photos, ' \
|
|
1077
|
-
'photos1, and photos2 as well as old test results', color = :blue
|
|
1078
|
-
FileUtils.rm_f Dir.glob('LOGS/*')
|
|
1079
|
-
FileUtils.rm_f Dir.glob('LOGS1/*')
|
|
1080
|
-
FileUtils.rm_f Dir.glob('LOGS2/*')
|
|
1081
|
-
FileUtils.rm_f Dir.glob('photos/*')
|
|
1082
|
-
FileUtils.rm_f Dir.glob('photos1/*')
|
|
1083
|
-
FileUtils.rm_f Dir.glob('photos2/*')
|
|
1084
|
-
|
|
1085
|
-
shell.say 'Removing files binary_history.data, out.txt, ' \
|
|
1086
|
-
'test_results.yml, and memory usage files', color = :blue
|
|
1087
|
-
FileUtils.rm_f 'binary_history.data'
|
|
1088
|
-
FileUtils.rm_f 'out.txt'
|
|
1089
|
-
FileUtils.rm_f 'test_results.yml'
|
|
1090
|
-
FileUtils.rm_f Dir.glob('mem-*.txt')
|
|
1091
|
-
if File.directory? File.join('star_history', 'history_out')
|
|
1092
|
-
shell.say 'Removing all files of the form history_out* from ' \
|
|
1093
|
-
'star_history', :blue
|
|
1094
|
-
FileUtils.rm_f Dir.glob(File.join('star_history', 'history_out', '*'))
|
|
1095
|
-
end
|
|
1096
|
-
if File.directory? File.join('star_profile', 'profiles_out')
|
|
1097
|
-
shell.say 'Removing all files of the form profiles_out* from ' \
|
|
1098
|
-
'star_profile', color = :blue
|
|
1099
|
-
FileUtils.rm_f Dir.glob(File.join('star_profile', 'profiles_out', '*'))
|
|
1100
|
-
end
|
|
1101
|
-
shell.say 'Removing .running', color = :blue
|
|
1102
|
-
FileUtils.rm_f '.running'
|
|
1103
|
-
end
|
|
1104
|
-
end
|
|
1105
|
-
|
|
1106
|
-
# based on $MESA_DIR/star/test_suite/each_test_run_and_diff, revision 10000
|
|
819
|
+
# just punt to +each_test_run+ in the test_suite directory. It's your problem
|
|
820
|
+
# now, sucker!
|
|
1107
821
|
def do_one
|
|
1108
822
|
shell.say("Testing #{test_name}", :yellow)
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
@test_omp_num_threads = omp_num_threads
|
|
1112
|
-
if mesa.using_sdk
|
|
1113
|
-
version_bin = File.join(ENV['MESASDK_ROOT'], 'bin', 'mesasdk_version')
|
|
1114
|
-
# can't use bash_execute because the return value of bash_execute is the
|
|
1115
|
-
# exit status of the commmand (true or false), whereas backticks give the
|
|
1116
|
-
# output (the version string) as the output
|
|
1117
|
-
if File.exist? version_bin
|
|
1118
|
-
# newer SDKs have a simple executable
|
|
1119
|
-
@compiler_version = `#{version_bin}`.strip
|
|
1120
|
-
else
|
|
1121
|
-
# older way, call bash on it (file is mesasdk_version.sh)
|
|
1122
|
-
@compiler_version = `bash -c #{version_bin + '.sh'}`.strip
|
|
1123
|
-
end
|
|
1124
|
-
|
|
1125
|
-
shell.say("Using version #{@compiler_version} of the SDK.", :blue)
|
|
1126
|
-
end
|
|
1127
|
-
in_dir do
|
|
1128
|
-
FileUtils.touch '.running'
|
|
1129
|
-
build_and_run
|
|
1130
|
-
# report memory usage if it is available
|
|
1131
|
-
if File.exist?('mem-rn.txt')
|
|
1132
|
-
@rn_mem = File.read('mem-rn.txt').strip.to_i
|
|
1133
|
-
end
|
|
1134
|
-
if File.exist?('mem-re.txt')
|
|
1135
|
-
@re_mem = File.read('mem-re.txt').strip.to_i
|
|
1136
|
-
end
|
|
1137
|
-
FileUtils.rm '.running'
|
|
1138
|
-
puts ''
|
|
1139
|
-
end
|
|
1140
|
-
test_finish = Time.now
|
|
1141
|
-
@total_runtime_seconds = (test_finish - test_start).to_i
|
|
1142
|
-
end
|
|
1143
|
-
|
|
1144
|
-
def log_results
|
|
1145
|
-
# gets all parameters that would be submitted as well as computer
|
|
1146
|
-
# information and dumps to a yml file in the test case directory
|
|
1147
|
-
save_file = File.join(test_case_dir, 'test_results.yml')
|
|
1148
|
-
shell.say "Logging test results to #{save_file}...", :blue
|
|
1149
|
-
res = {
|
|
1150
|
-
'test_case' => test_name,
|
|
1151
|
-
'module' => mod,
|
|
1152
|
-
'runtime_seconds' => runtime_seconds,
|
|
1153
|
-
're_time' => re_time,
|
|
1154
|
-
'total_runtime_seconds' => total_runtime_seconds,
|
|
1155
|
-
'mesa_version' => mesa_version,
|
|
1156
|
-
'outcome' => outcome,
|
|
1157
|
-
'omp_num_threads' => test_omp_num_threads,
|
|
1158
|
-
'success_type' => success_type,
|
|
1159
|
-
'failure_type' => failure_type,
|
|
1160
|
-
'runtime_minutes' => runtime_minutes,
|
|
1161
|
-
'retries' => retries,
|
|
1162
|
-
'backups' => backups,
|
|
1163
|
-
'steps' => steps,
|
|
1164
|
-
'diff' => diff,
|
|
1165
|
-
'checksum' => checksum,
|
|
1166
|
-
'rn_mem' => rn_mem,
|
|
1167
|
-
're_mem' => re_mem,
|
|
1168
|
-
'summary_text' => summary_text
|
|
1169
|
-
}
|
|
1170
|
-
if compiler == 'SDK'
|
|
1171
|
-
res['compiler'] = 'SDK'
|
|
1172
|
-
res['compiler_version'] = compiler_version
|
|
1173
|
-
end
|
|
1174
|
-
File.open(save_file, 'w') { |f| f.write(YAML.dump(res)) }
|
|
1175
|
-
shell.say "Successfully saved results to file #{save_file}.\n", :green
|
|
1176
|
-
end
|
|
1177
|
-
|
|
1178
|
-
def load_results
|
|
1179
|
-
# loads all parameters from a previous test run, likely for submission
|
|
1180
|
-
# purposes
|
|
1181
|
-
load_file = File.join(test_case_dir, 'test_results.yml')
|
|
1182
|
-
shell.say "Loading data from #{load_file}...", :blue
|
|
1183
|
-
unless File.exist? load_file
|
|
1184
|
-
shell.say "No such file: #{load_file}. No data loaded.", :red
|
|
1185
|
-
return
|
|
1186
|
-
end
|
|
1187
|
-
data = YAML.safe_load(File.read(load_file), [Symbol])
|
|
1188
|
-
@runtime_seconds = data['runtime_seconds'] || @runtime_seconds
|
|
1189
|
-
@re_time = data['re_time'] || @re_time
|
|
1190
|
-
@total_runtime_seconds = data['total_runtime_seconds'] || @total_runtime_seconds
|
|
1191
|
-
@mod = data['module'] || @mod
|
|
1192
|
-
@mesa_version = data['mesa_version'] || @mesa_version
|
|
1193
|
-
@outcome = data['outcome'] || @outcome
|
|
1194
|
-
@test_omp_num_threads = data['omp_num_threads'] || @test_omp_num_threads
|
|
1195
|
-
@success_type = data['success_type'] || @success_type
|
|
1196
|
-
@failure_type = data['failure_type'] || @failure_type
|
|
1197
|
-
@runtime_minutes = data['runtime_minutes'] || @runtime_minutes
|
|
1198
|
-
@retries = data['retries'] || @retries
|
|
1199
|
-
@backups = data['backups'] || @backups
|
|
1200
|
-
@steps = data['steps'] || @steps
|
|
1201
|
-
@diff = data['diff'] || @diff
|
|
1202
|
-
@checksum = data['checksum'] || @checksum
|
|
1203
|
-
@rn_mem = data['rn_mem'] || @rn_mem
|
|
1204
|
-
@re_mem = data['re_mem'] || @re_mem
|
|
1205
|
-
@summary_text = data['summary_text'] || @summary_text
|
|
1206
|
-
@compiler = data['compiler'] || @compiler
|
|
1207
|
-
|
|
1208
|
-
@compiler_version = data['compiler_version'] || @compiler_version
|
|
1209
|
-
|
|
1210
|
-
# convert select data to symbols since that is how they are used
|
|
1211
|
-
@outcome = @outcome.to_sym if @outcome
|
|
1212
|
-
@success_type = @success_type.to_sym if @success_type
|
|
1213
|
-
@failure_type = @failure_type.to_sym if @failure_type
|
|
1214
|
-
|
|
1215
|
-
shell.say "Done loading data from #{load_file}.\n", :green
|
|
1216
|
-
end
|
|
1217
|
-
|
|
1218
|
-
def load_summary_data
|
|
1219
|
-
begin
|
|
1220
|
-
out_data = parse_out
|
|
1221
|
-
@summary_text = get_summary_text
|
|
1222
|
-
rescue Errno::ENOENT
|
|
1223
|
-
shell.say "\nError loading data from #{out_file}. No summary data "\
|
|
1224
|
-
'loaded. Proceeding anyway.', :red
|
|
1225
|
-
else
|
|
1226
|
-
@runtime_minutes = out_data[:runtime_minutes]
|
|
1227
|
-
@retries = out_data[:retries]
|
|
1228
|
-
@backups = out_data[:backups]
|
|
1229
|
-
@steps = out_data[:steps]
|
|
823
|
+
visit_dir(test_suite_dir) do
|
|
824
|
+
bash_execute("./each_test_run #{position}")
|
|
1230
825
|
end
|
|
1231
826
|
end
|
|
1232
827
|
|
|
1233
|
-
def
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
run_summaries.each do |summary|
|
|
1239
|
-
summary =~ /^\s*runtime\s*\(minutes\),\s+retries,\s+backups,\ssteps\s+(\d+\.?\d*)\s+(\d+)\s+(\d+)\s+(\d+)\s*$/
|
|
1240
|
-
runtime_minutes += $1.to_f
|
|
1241
|
-
retries += $2.to_i
|
|
1242
|
-
backups += $3.to_i
|
|
1243
|
-
steps += $4.to_i
|
|
828
|
+
def results_hash
|
|
829
|
+
testhub_file = File.join(test_case_dir, 'testhub.yml')
|
|
830
|
+
unless File.exist?(testhub_file)
|
|
831
|
+
raise TestCaseDirError.new('No results found for test case '\
|
|
832
|
+
"#{test_name}.")
|
|
1244
833
|
end
|
|
1245
|
-
|
|
1246
|
-
steps: steps}
|
|
834
|
+
YAML.safe_load(File.read(testhub_file), [Symbol])
|
|
1247
835
|
end
|
|
1248
836
|
|
|
1249
837
|
private
|
|
1250
838
|
|
|
1251
|
-
def data_types
|
|
1252
|
-
%i[float integer string boolean]
|
|
1253
|
-
end
|
|
1254
|
-
|
|
1255
|
-
def rn_time
|
|
1256
|
-
runtime_seconds
|
|
1257
|
-
end
|
|
1258
|
-
|
|
1259
839
|
# cd into the test case directory, do something in a block, then cd back
|
|
1260
840
|
# to original directory
|
|
1261
841
|
def in_dir(&block)
|
|
@@ -1265,326 +845,14 @@ class MesaTestCase
|
|
|
1265
845
|
# make sure that we can get to the test case directory. Throw an exception
|
|
1266
846
|
# if we cannot
|
|
1267
847
|
def check_test_case
|
|
1268
|
-
return if
|
|
848
|
+
return if dir_or_symlink_exists? test_case_dir
|
|
1269
849
|
raise TestCaseDirError, "No such test case: #{test_case_dir}."
|
|
1270
850
|
end
|
|
1271
851
|
|
|
1272
|
-
# verify that mesa_dir is valid by checking for
|
|
1273
|
-
# directory
|
|
852
|
+
# "verify" that mesa_dir is valid by checking for test_suite directory
|
|
1274
853
|
def check_mesa_dir
|
|
1275
|
-
is_valid =
|
|
1276
|
-
|
|
1277
|
-
raise MesaDirError, "Invalid MESA dir: #{mesa_dir}" unless is_valid
|
|
1278
|
-
end
|
|
1279
|
-
|
|
1280
|
-
# append message to log file
|
|
1281
|
-
def log_message(msg, color = nil, log_file = 'out.txt')
|
|
1282
|
-
if color.nil?
|
|
1283
|
-
shell.say(msg)
|
|
1284
|
-
else
|
|
1285
|
-
shell.say(msg, color)
|
|
1286
|
-
end
|
|
1287
|
-
File.open(log_file, 'a') { |f| f.puts(msg) }
|
|
1288
|
-
end
|
|
1289
|
-
|
|
1290
|
-
# write failure message to log file
|
|
1291
|
-
def write_failure_message
|
|
1292
|
-
msg = "******************** #{failure_msg[@failure_type]} " \
|
|
1293
|
-
'********************'
|
|
1294
|
-
log_message(msg, :red)
|
|
1295
|
-
end
|
|
1296
|
-
|
|
1297
|
-
# write success message to log file
|
|
1298
|
-
def write_success_msg(success_type)
|
|
1299
|
-
msg = 'PASS ' + success_msg[success_type]
|
|
1300
|
-
log_message(msg, :green)
|
|
1301
|
-
end
|
|
1302
|
-
|
|
1303
|
-
# used as return value for run or photo test. Logs failure to text file, and
|
|
1304
|
-
# sets internal status to failing
|
|
1305
|
-
def fail_test(failure_type)
|
|
1306
|
-
@failure_type = failure_type
|
|
1307
|
-
@outcome = :fail
|
|
1308
|
-
write_failure_message
|
|
1309
|
-
false
|
|
1310
|
-
end
|
|
1311
|
-
|
|
1312
|
-
# used as return value for run or photo test. Logs data to text file, and
|
|
1313
|
-
# sets internal status to passing
|
|
1314
|
-
def succeed(success_type)
|
|
1315
|
-
@success_type = success_type
|
|
1316
|
-
@outcome = :pass
|
|
1317
|
-
# this should ONLY be read after we are certain we've passed AND that we
|
|
1318
|
-
# even have a newly-made checksum
|
|
1319
|
-
if File.exist?('checks.md5') && @mesa.update_checksums
|
|
1320
|
-
# sometimes we want to ignore the final checksum value
|
|
1321
|
-
# we still want to check that restarts on individual machines are bit-for-bit
|
|
1322
|
-
# but we don't require bit-for-bit globally, so we report a bogus checksum
|
|
1323
|
-
if File.exist?('.ignore_checksum') then
|
|
1324
|
-
@checksum ='00000000000000000000000000000000'
|
|
1325
|
-
else
|
|
1326
|
-
@checksum = File.read('checks.md5').split.first
|
|
1327
|
-
end
|
|
1328
|
-
end
|
|
1329
|
-
write_success_msg(success_type)
|
|
1330
|
-
true
|
|
1331
|
-
end
|
|
1332
|
-
|
|
1333
|
-
def check_run
|
|
1334
|
-
# assumes we are in the directory already, called from something else
|
|
1335
|
-
run_start = Time.now
|
|
1336
|
-
|
|
1337
|
-
# do the run
|
|
1338
|
-
rn_command = if ENV['MESASDK_ROOT'] && File.exist?(File.join(ENV['MESASDK_ROOT'], 'bin', 'time'))
|
|
1339
|
-
%q(command time -f '%M' -o mem-rn.txt ./rn > out.txt 2> ) +
|
|
1340
|
-
'err.txt'
|
|
1341
|
-
else
|
|
1342
|
-
'./rn >> out.txt 2> err.txt'
|
|
1343
|
-
end
|
|
1344
|
-
puts rn_command
|
|
1345
|
-
bash_execute(rn_command)
|
|
1346
|
-
|
|
1347
|
-
# report runtime and clean up
|
|
1348
|
-
run_finish = Time.now
|
|
1349
|
-
@runtime_seconds = (run_finish - run_start).to_i
|
|
1350
|
-
@rn_time = (run_finish - run_start).to_i
|
|
1351
|
-
shell.say("Finished with ./rn; runtime = #{@runtime_seconds} seconds.",
|
|
1352
|
-
:blue)
|
|
1353
|
-
append_and_rm_err
|
|
1354
|
-
|
|
1355
|
-
# look for success text
|
|
1356
|
-
success = true
|
|
1357
|
-
File.open('out.txt', 'r') do |f|
|
|
1358
|
-
success = !f.read.downcase.scan(success_string.downcase).empty?
|
|
1359
|
-
end
|
|
1360
|
-
# bail if there was no test string found
|
|
1361
|
-
return fail_test(:run_test_string) unless success
|
|
1362
|
-
|
|
1363
|
-
# no final model to check, and we already found the test string, so pass
|
|
1364
|
-
return succeed(:run_test_string) unless final_model
|
|
1365
|
-
|
|
1366
|
-
# display runtime message
|
|
1367
|
-
puts IO.readlines('out.txt').select { |line| line.scan(/runtime/i) }[-1]
|
|
1368
|
-
|
|
1369
|
-
# there's supposed to be a final model; check that it exists first
|
|
1370
|
-
return fail_test(:final_model) unless File.exist?(final_model)
|
|
1371
|
-
|
|
1372
|
-
# update checksums
|
|
1373
|
-
#
|
|
1374
|
-
# if this is true, behave like each_test_run. update the checksum
|
|
1375
|
-
# after rn and then check it matches after re
|
|
1376
|
-
#
|
|
1377
|
-
# if this is false, behave like each_test_run_and_diff. assume
|
|
1378
|
-
# the checksum is up-to-date and check it matches after rn and re.
|
|
1379
|
-
if @mesa.update_checksums
|
|
1380
|
-
@diff = 0 # this means no diffs run
|
|
1381
|
-
puts "md5sum \"#{final_model}\" > checks.md5"
|
|
1382
|
-
bash_execute("md5sum \"#{final_model}\" > checks.md5")
|
|
1383
|
-
FileUtils.cp final_model, 'final_check.mod'
|
|
1384
|
-
|
|
1385
|
-
# if there's no photo, we won't check the checksum, so we've succeeded
|
|
1386
|
-
return succeed(:run_test_string) unless photo
|
|
1387
|
-
# if there is a photo, we'll have to wait and see
|
|
1388
|
-
return true
|
|
1389
|
-
end
|
|
1390
|
-
|
|
1391
|
-
# check that final model matches
|
|
1392
|
-
@diff = 1 # this means diffs were run
|
|
1393
|
-
puts './ck >& final_check_diff.txt'
|
|
1394
|
-
return fail_test(:run_checksum) unless
|
|
1395
|
-
bash_execute('./ck >& final_check_diff.txt')
|
|
1396
|
-
return fail_test(:run_diff) if File.exist?('final_check_diff.txt') &&
|
|
1397
|
-
!File.read('final_check_diff.txt').empty?
|
|
1398
|
-
return succeed(:run_checksum) if File.exist? final_model
|
|
1399
|
-
end
|
|
1400
|
-
|
|
1401
|
-
# prepare for and do restart, check results, and return pass/fail status
|
|
1402
|
-
def check_restart
|
|
1403
|
-
# abort if there is not photo specified
|
|
1404
|
-
return unless photo
|
|
1405
|
-
|
|
1406
|
-
# get penultimate photo
|
|
1407
|
-
if photo == "auto" then
|
|
1408
|
-
# get all photos [single-star (x100) or binary (b_x100); exclude binary stars (1_x100, 2_x100)]
|
|
1409
|
-
photo_files = Dir["photos/*"].select{|p| p =~ /^photos\/(b_)?x?\d+$/}
|
|
1410
|
-
# pull out 2nd most recent one
|
|
1411
|
-
re_photo = File.basename(photo_files.sort_by { |file_name| File.stat(file_name).mtime } [-2])
|
|
1412
|
-
# if binary, trim off prefix
|
|
1413
|
-
re_photo.delete_prefix!("b_")
|
|
1414
|
-
else
|
|
1415
|
-
re_photo = photo
|
|
1416
|
-
end
|
|
1417
|
-
|
|
1418
|
-
# check that photo file actually exists
|
|
1419
|
-
unless File.exist?(File.join('photos', re_photo)) ||
|
|
1420
|
-
File.exist?(File.join('photos1', re_photo)) ||
|
|
1421
|
-
File.exist?(File.join('photos', "b_#{re_photo}"))
|
|
1422
|
-
return fail_test(:photo_file)
|
|
1423
|
-
end
|
|
1424
|
-
|
|
1425
|
-
# remove final model since it will be remade by restart
|
|
1426
|
-
FileUtils.rm_f final_model
|
|
1427
|
-
|
|
1428
|
-
# do restart and consolidate output. Command depends on if we have access
|
|
1429
|
-
# to SDK version of gnu time.
|
|
1430
|
-
re_command = if ENV['MESASDK_ROOT'] && File.exist?(File.join(ENV['MESASDK_ROOT'], 'bin', 'time'))
|
|
1431
|
-
%q(command time -f '%M' -o mem-re.txt ./re ) + "#{re_photo}" \
|
|
1432
|
-
' >> out.txt 2> err.txt'
|
|
1433
|
-
else
|
|
1434
|
-
"./re #{re_photo} >> out.txt 2> err.txt"
|
|
1435
|
-
end
|
|
1436
|
-
|
|
1437
|
-
puts re_command
|
|
1438
|
-
# puts "./re #{re_photo} >> out.txt 2> err.txt"
|
|
1439
|
-
re_start = Time.now
|
|
1440
|
-
# bash_execute("./re #{re_photo} >> out.txt 2> err.txt")
|
|
1441
|
-
# bash_execute(%Q{command time -f '%M' -o mem-re.txt ./re #{re_photo} >> out.txt 2> err.txt})
|
|
1442
|
-
bash_execute(re_command)
|
|
1443
|
-
re_finish = Time.now
|
|
1444
|
-
@re_time = (re_finish - re_start).to_i
|
|
1445
|
-
append_and_rm_err
|
|
1446
|
-
|
|
1447
|
-
# check that final model matches
|
|
1448
|
-
puts './ck >& final_check_diff.txt'
|
|
1449
|
-
return fail_test(:photo_checksum) unless
|
|
1450
|
-
bash_execute('./ck >& final_check_diff.txt')
|
|
1451
|
-
return fail_test(:photo_diff) if
|
|
1452
|
-
File.exist?('final_check_diff.txt') &&
|
|
1453
|
-
!File.read('final_check_diff.txt').empty?
|
|
1454
|
-
succeed(:photo_checksum)
|
|
1455
|
-
end
|
|
1456
|
-
|
|
1457
|
-
def build_and_run
|
|
1458
|
-
# assumes we are in the test case directory. Should only be called
|
|
1459
|
-
# in the context of an `in_dir` block.
|
|
1460
|
-
|
|
1461
|
-
# first clean and make... Should be compatible with any shell since
|
|
1462
|
-
# redirection is always wrapped in 'bash -c "{STUFF}"'
|
|
1463
|
-
simple_clean
|
|
1464
|
-
begin
|
|
1465
|
-
mk
|
|
1466
|
-
rescue TestCaseDirError
|
|
1467
|
-
return fail_test(:compilation)
|
|
1468
|
-
end
|
|
1469
|
-
|
|
1470
|
-
# remove old final model if it exists
|
|
1471
|
-
remove_final_model
|
|
1472
|
-
|
|
1473
|
-
# only check restart/photo if we get through run successfully
|
|
1474
|
-
check_restart if check_run
|
|
1475
|
-
|
|
1476
|
-
# get reported runtime, retries, backups, and steps
|
|
1477
|
-
load_summary_data if File.exist?(out_file)
|
|
1478
|
-
end
|
|
1479
|
-
|
|
1480
|
-
# append contents of err.txt to end of out.txt, then delete err.txt
|
|
1481
|
-
def append_and_rm_err(outfile = 'out.txt', errfile = 'err.txt')
|
|
1482
|
-
err_contents = File.read(errfile)
|
|
1483
|
-
display_errors(err_contents)
|
|
1484
|
-
log_errors(err_contents, outfile)
|
|
1485
|
-
FileUtils.rm errfile
|
|
1486
|
-
end
|
|
1487
|
-
|
|
1488
|
-
def display_errors(err_contents)
|
|
1489
|
-
return if err_contents.strip.empty?
|
|
1490
|
-
shell.say("\nERRORS", :red)
|
|
1491
|
-
puts err_contents
|
|
1492
|
-
shell.say('END OF ERRORS', :red)
|
|
1493
|
-
end
|
|
1494
|
-
|
|
1495
|
-
def log_errors(err_contents, outfile)
|
|
1496
|
-
return if err_contents.strip.empty?
|
|
1497
|
-
File.open(outfile, 'a') { |f_out| f_out.write(err_contents) }
|
|
1498
|
-
shell.say("appended to #{outfile}\n", :red)
|
|
1499
|
-
end
|
|
1500
|
-
|
|
1501
|
-
def simple_clean
|
|
1502
|
-
puts './clean'
|
|
1503
|
-
return if bash_execute('./clean')
|
|
1504
|
-
raise TestCaseDirError, 'Encountered an error when running `clean` in ' \
|
|
1505
|
-
"#{Dir.getwd} for test case #{test_name}."
|
|
1506
|
-
end
|
|
1507
|
-
|
|
1508
|
-
def mk
|
|
1509
|
-
puts './mk > mk.txt'
|
|
1510
|
-
unless bash_execute('./mk > mk.txt')
|
|
1511
|
-
raise TestCaseDirError, 'Encountered an error when running `mk` in ' \
|
|
1512
|
-
"#{Dir.getwd} for test case #{test_name}."
|
|
1513
|
-
end
|
|
1514
|
-
FileUtils.rm 'mk.txt'
|
|
1515
|
-
end
|
|
1516
|
-
|
|
1517
|
-
def remove_final_model
|
|
1518
|
-
# remove final model if it already exists
|
|
1519
|
-
return unless final_model
|
|
1520
|
-
return unless File.exist?(final_model)
|
|
1521
|
-
FileUtils.rm(final_model)
|
|
1522
|
-
end
|
|
1523
|
-
|
|
1524
|
-
def out_file
|
|
1525
|
-
File.join(test_case_dir, 'out.txt')
|
|
1526
|
-
end
|
|
1527
|
-
|
|
1528
|
-
# helpers for getting run summaries
|
|
1529
|
-
def run_summaries
|
|
1530
|
-
# look at all lines in out.txt
|
|
1531
|
-
lines = IO.readlines(out_file)
|
|
1532
|
-
|
|
1533
|
-
# find lines with summary information
|
|
1534
|
-
summary_line_numbers = []
|
|
1535
|
-
lines.each_with_index do |line, i|
|
|
1536
|
-
if line =~ /^\s*runtime \(minutes\),\s+retries,\s+backups,\ssteps/
|
|
1537
|
-
summary_line_numbers << i
|
|
1538
|
-
end
|
|
1539
|
-
end
|
|
1540
|
-
|
|
1541
|
-
# find lines indicating passage or failure of runs and restarts
|
|
1542
|
-
run_finish_line_numbers = []
|
|
1543
|
-
restart_finish_line_numbers = []
|
|
1544
|
-
lines.each_with_index do |line, i|
|
|
1545
|
-
if line =~ /^\s*((?:PASS)|(?:FAIL))\s+#{test_name}\s+restart/
|
|
1546
|
-
restart_finish_line_numbers << i
|
|
1547
|
-
elsif line =~ /^\s*((?:PASS)|(?:FAIL))\s+#{test_name}\s+run/
|
|
1548
|
-
run_finish_line_numbers << i
|
|
1549
|
-
end
|
|
1550
|
-
end
|
|
1551
|
-
|
|
1552
|
-
# only keep summaries that correspond to runs rather than restart
|
|
1553
|
-
summary_line_numbers.select do |i|
|
|
1554
|
-
run_summary?(i, run_finish_line_numbers, restart_finish_line_numbers)
|
|
1555
|
-
end.map { |line_number| lines[line_number] }
|
|
1556
|
-
end
|
|
1557
|
-
|
|
1558
|
-
def get_summary_text
|
|
1559
|
-
# original plan was to include diff data in summary text... now it's just
|
|
1560
|
-
# part of the test_instance object and is submitted as an integer
|
|
1561
|
-
# res = case diff
|
|
1562
|
-
# when 0
|
|
1563
|
-
# "No Diff\n"
|
|
1564
|
-
# when 1
|
|
1565
|
-
# "Diff\n"
|
|
1566
|
-
# else
|
|
1567
|
-
# "Ambiguous Diff\n"
|
|
1568
|
-
# end
|
|
1569
|
-
# res +
|
|
1570
|
-
IO.readlines(out_file).select do |line|
|
|
1571
|
-
line =~ /^\s*runtime/
|
|
1572
|
-
end.join
|
|
1573
|
-
end
|
|
1574
|
-
|
|
1575
|
-
def run_summary?(i, run_finish_line_numbers, restart_finish_line_numbers)
|
|
1576
|
-
# iterate from starting line (a summary line) up to largest PASS/FAIL
|
|
1577
|
-
# line, bail out if summary line is beyond any PASS/FAIL line
|
|
1578
|
-
max_line = run_finish_line_numbers.max || 0
|
|
1579
|
-
max_line = [max_line, (restart_finish_line_numbers.max || 0)].max
|
|
1580
|
-
return false if i > max_line
|
|
1581
|
-
# return true if next PASS/FAIL line is for a run and fail if it is for a
|
|
1582
|
-
# restart
|
|
1583
|
-
i.upto(max_line) do |j|
|
|
1584
|
-
return true if run_finish_line_numbers.include?(j)
|
|
1585
|
-
return false if restart_finish_line_numbers.include?(j)
|
|
1586
|
-
end
|
|
1587
|
-
false
|
|
854
|
+
is_valid = dir_or_symlink_exists? test_suite_dir
|
|
855
|
+
raise MesaDirError, "Invalid MESA dir: #{mesa.mesa_dir}" unless is_valid
|
|
1588
856
|
end
|
|
1589
857
|
|
|
1590
858
|
end
|
|
@@ -1599,13 +867,11 @@ end
|
|
|
1599
867
|
def visit_and_check(new_dir, exception, message)
|
|
1600
868
|
cwd = Dir.getwd
|
|
1601
869
|
shell.say "Leaving #{cwd}", :blue
|
|
1602
|
-
|
|
1603
|
-
shell.say "Entering #{new_dir}.", :blue
|
|
870
|
+
shell.say "\nEntering #{new_dir}.", :blue
|
|
1604
871
|
Dir.chdir(new_dir)
|
|
1605
872
|
success = yield if block_given?
|
|
1606
873
|
shell.say "Leaving #{new_dir}", :blue
|
|
1607
|
-
|
|
1608
|
-
shell.say "Entering #{cwd}.", :blue
|
|
874
|
+
shell.say "\nEntering #{cwd}.", :blue
|
|
1609
875
|
Dir.chdir(cwd)
|
|
1610
876
|
return if success
|
|
1611
877
|
raise exception, message
|
|
@@ -1613,18 +879,18 @@ end
|
|
|
1613
879
|
|
|
1614
880
|
# cd into a new directory, execute a block, then cd back into original
|
|
1615
881
|
# directory
|
|
1616
|
-
def visit_dir(new_dir)
|
|
882
|
+
def visit_dir(new_dir, quiet: false)
|
|
1617
883
|
cwd = Dir.getwd
|
|
1618
|
-
shell.say "Leaving #{cwd}\n", :blue
|
|
1619
|
-
shell.say "
|
|
884
|
+
shell.say "Leaving #{cwd}\n", :blue unless quiet
|
|
885
|
+
shell.say "\nEntering #{new_dir}.", :blue unless quiet
|
|
1620
886
|
Dir.chdir(new_dir)
|
|
1621
887
|
yield if block_given?
|
|
1622
|
-
shell.say "Leaving #{new_dir}\n", :blue
|
|
1623
|
-
shell.say "
|
|
1624
|
-
puts ""
|
|
888
|
+
shell.say "Leaving #{new_dir}\n", :blue unless quiet
|
|
889
|
+
shell.say "\nRe-entering #{cwd}.", :blue unless quiet
|
|
1625
890
|
Dir.chdir(cwd)
|
|
1626
891
|
end
|
|
1627
892
|
|
|
893
|
+
# the next function probalby doesn't belong here, but keep it anyway, please
|
|
1628
894
|
# create seed data for test cases for MesaTestHub of a given mesa version
|
|
1629
895
|
def generate_seeds_rb(mesa_dir, outfile)
|
|
1630
896
|
m = Mesa.new(mesa_dir: mesa_dir)
|
|
@@ -1635,7 +901,6 @@ def generate_seeds_rb(mesa_dir, outfile)
|
|
|
1635
901
|
m.test_names.each do |test_case_name|
|
|
1636
902
|
f.puts ' {'
|
|
1637
903
|
f.puts " name: '#{test_case_name}',"
|
|
1638
|
-
f.puts " version_added: #{m.version_number},"
|
|
1639
904
|
# no comma on last one
|
|
1640
905
|
if test_case_name == m.test_names[-1]
|
|
1641
906
|
f.puts(' }')
|
|
@@ -1648,7 +913,22 @@ def generate_seeds_rb(mesa_dir, outfile)
|
|
|
1648
913
|
end
|
|
1649
914
|
end
|
|
1650
915
|
|
|
916
|
+
def dir_or_symlink_exists?(path)
|
|
917
|
+
File.directory?(path) || File.symlink?(path)
|
|
918
|
+
end
|
|
919
|
+
|
|
1651
920
|
# force the execution to happen with bash
|
|
1652
|
-
def bash_execute(command)
|
|
1653
|
-
system('bash -c "' + command + '"')
|
|
921
|
+
def bash_execute(command, throw_exception=false)
|
|
922
|
+
res = system('bash -c "' + command + '"')
|
|
923
|
+
if !res && throw_exception
|
|
924
|
+
raise BashError('Encountered an error when executing the following '\
|
|
925
|
+
"command in bash: #{command}.")
|
|
926
|
+
end
|
|
927
|
+
res
|
|
928
|
+
end
|
|
929
|
+
|
|
930
|
+
# force execution to happen with bash, but return result rather than exit
|
|
931
|
+
# status (like backticks)
|
|
932
|
+
def bashticks(command)
|
|
933
|
+
`bash -c "#{command}"`.chomp
|
|
1654
934
|
end
|