mesa_test 0.0.3
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 +7 -0
- data/bin/mesa_test +221 -0
- data/lib/mesa_test.rb +996 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a51f1b2345eafea6517e911afc3aabaaacd45356
|
4
|
+
data.tar.gz: b3e90e330c892fe05a0fc47f8d0c338f35f8085e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2d775bcc97dd0d959dea7db95cb454163a402b94fb9f73cd84509ed9c596303a9e456ddfccc6ece2f9e8a352f6309cdecb57ab0870c78ef0457b6812afc506d2
|
7
|
+
data.tar.gz: cab50479ae7d1cefd42359a4de2e0bfc845d003d7f0533677710f57263e59778fa648b34e54df849389bef1d60e2ed679c1b00a9ee12391940cec3e598a602a6
|
data/bin/mesa_test
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'mesa_test'
|
4
|
+
require 'thor'
|
5
|
+
|
6
|
+
class MesaTest < Thor
|
7
|
+
|
8
|
+
desc "setup [CONFIG_FILE]", "Setup MesaTestHub config file."
|
9
|
+
long_desc <<-LONGDESC
|
10
|
+
If optional CONFIG_FILE is provided, search for that file and load it
|
11
|
+
to provide default values for configuration. If not provided, the default
|
12
|
+
file, ~/.mesa_test.yml, is used. The resulting config file is saved to
|
13
|
+
the same location it was read from.
|
14
|
+
LONGDESC
|
15
|
+
def setup(config_file=File.join(ENV['HOME'], '.mesa_test.yml'))
|
16
|
+
MesaTestSubmitter.new_from_config(config_file: config_file)
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
desc "test_one MESA_DIR TEST_CASE", "run, check, and submit one test case"
|
22
|
+
long_desc <<-LONGDESC
|
23
|
+
Run and check TEST_CASE, which resides in MESA_DIR/star/test_suite. Then
|
24
|
+
report results to MesaTestHub.
|
25
|
+
|
26
|
+
With --force option, skip confirmation of computer details, assuming values
|
27
|
+
in ~/.mesa_test.yml are correct.
|
28
|
+
|
29
|
+
With --log option, save yml file of test results in test directory, on
|
30
|
+
by default. Shut off with --no-log.
|
31
|
+
|
32
|
+
With --submit option, upload results to MESATestHub. By default, this is on.
|
33
|
+
To run without submission, use --no-submit.
|
34
|
+
LONGDESC
|
35
|
+
option :force, type: :boolean
|
36
|
+
option :log, type: :boolean, default: true
|
37
|
+
option :submit, type: :boolean, default: true
|
38
|
+
def test_one(mesa_dir, test_case_name)
|
39
|
+
|
40
|
+
if options[:submit]
|
41
|
+
s = MesaTestSubmitter.new_from_config
|
42
|
+
unless options[:force]
|
43
|
+
unless s.confirm_computer_data
|
44
|
+
s.setup
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
check_user_and_computer s
|
49
|
+
end
|
50
|
+
|
51
|
+
# set up and check mesa directory (doesn't really check to see if it's
|
52
|
+
# installed, just to see if it has a version number and a test suite
|
53
|
+
# directory)
|
54
|
+
m = Mesa.new mesa_dir: mesa_dir
|
55
|
+
raise MesaDirError, "Invalid MESA_DIR: #{mesa_dir}. Please download and " +
|
56
|
+
"install a valid MESA version or provide the path to one." unless
|
57
|
+
m.installed?
|
58
|
+
m.load_test_source_data
|
59
|
+
|
60
|
+
# make sure the test case is valid
|
61
|
+
t = m.find_test_case test_case_name: test_case_name
|
62
|
+
if t.nil?
|
63
|
+
msg = "No such test case, #{test_case_name} found in any of "
|
64
|
+
msg << MesaTestCase.modules.map do |mod|
|
65
|
+
File.join(m.test_suite_dir(mod: mod), 'do1_test_source')
|
66
|
+
end.join(' or ')
|
67
|
+
msg << '.'
|
68
|
+
raise TestCaseDirError, msg
|
69
|
+
end
|
70
|
+
|
71
|
+
# clean and run test
|
72
|
+
t.clean
|
73
|
+
t.do_one
|
74
|
+
|
75
|
+
# log results
|
76
|
+
t.log_results if options[:log]
|
77
|
+
|
78
|
+
# submit results
|
79
|
+
if options[:submit]
|
80
|
+
print 'Submitting results to ' + s.base_uri + '... '
|
81
|
+
s.submit(t)
|
82
|
+
puts "Done."
|
83
|
+
puts ""
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
desc "test_all MESA_DIR", "run, check, and submit all test cases"
|
89
|
+
long_desc <<-LONGDESC
|
90
|
+
Run and check all test cases residing in MESA_DIR/star/test_suite. Then
|
91
|
+
report results to MesaTestHub. Specifically, runs and checks all tests
|
92
|
+
detailed in MESA_DIR/star/test_suite/do1_test_source.
|
93
|
+
|
94
|
+
With --force option, skip confirmation of computer details, assuming values
|
95
|
+
in ~/.mesa_test.yml are correct. Only relevant if --submit option is on
|
96
|
+
(by default it is).
|
97
|
+
|
98
|
+
With --log option, save yml file of test results in test directory and a
|
99
|
+
summary in the test suite directory. On by default. Shut off with --no-log.
|
100
|
+
|
101
|
+
With --submit option, upload results to MESATestHub. By default, this is on.
|
102
|
+
To run without submission, use --no-submit.
|
103
|
+
LONGDESC
|
104
|
+
option :force, type: :boolean
|
105
|
+
option :log, type: :boolean, default: true
|
106
|
+
option :submit, type: :boolean, default: true
|
107
|
+
def test_all(mesa_dir)
|
108
|
+
|
109
|
+
if options[:submit]
|
110
|
+
s = MesaTestSubmitter.new_from_config
|
111
|
+
unless options[:force]
|
112
|
+
unless s.confirm_computer_data
|
113
|
+
s.setup
|
114
|
+
end
|
115
|
+
end
|
116
|
+
check_user_and_computer s
|
117
|
+
end
|
118
|
+
|
119
|
+
# set up and check mesa directory (doesn't really check to see if it's
|
120
|
+
# installed, just to see if it has a version number and a test suite
|
121
|
+
# directory)
|
122
|
+
m = Mesa.new mesa_dir: mesa_dir
|
123
|
+
raise MesaDirError, "Invalid MESA_DIR: #{mesa_dir}. Please download and " +
|
124
|
+
"install a valid MESA version or provide the path to one." unless
|
125
|
+
m.installed?
|
126
|
+
m.load_test_source_data
|
127
|
+
|
128
|
+
# run all tests
|
129
|
+
m.each_test_run_and_diff(log_results: options[:log])
|
130
|
+
|
131
|
+
# submit all tests
|
132
|
+
s.submit_all(m) if options[:submit]
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
desc "install VERSION_NUMBER MESA_DIR", 'download and install mesa release '+
|
137
|
+
'VERSION_NUMBER to directory MESA_DIR'
|
138
|
+
long_desc <<-LONGDESC
|
139
|
+
Calls to svn to install mesa release VERSION_NUMBER into the directory
|
140
|
+
MESA_DIR. Basically just an svn checkout followed by going into the directory
|
141
|
+
and running ./clean and ./install. SDK or compilers must be set up prior.
|
142
|
+
Does not affect the user's MESA_DIR or other environment variables.
|
143
|
+
LONGDESC
|
144
|
+
def install(version, mesa_dir)
|
145
|
+
m = Mesa.download(version_number:version, new_mesa_dir: mesa_dir)
|
146
|
+
m.clean
|
147
|
+
m.install
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
desc "install_and_run_all VERSION_NUMBER MESA_DIR", 'download and install ' +
|
152
|
+
'mesa release VERSION_NUMBER to directory MESA_DIR and run/submit all tests'
|
153
|
+
long_desc <<-LONGDESC
|
154
|
+
Calls to svn to install mesa release VERSION_NUMBER into the directory
|
155
|
+
MESA_DIR. Basically just an svn checkout followed by going into the directory
|
156
|
+
and running ./clean and ./install. SDK or compilers must be set up prior.
|
157
|
+
Once installation is complete, run the test suite, and report results to
|
158
|
+
MesaTestHub. Does not affect the user's MESA_DIR or other environmnet
|
159
|
+
variables. This is bascially a shortcut for running
|
160
|
+
|
161
|
+
`mesa_test install SOME_VERSION SOME_DIR`
|
162
|
+
|
163
|
+
followed by
|
164
|
+
|
165
|
+
`mesa_test test_all SOME_DIR`
|
166
|
+
|
167
|
+
Use flag --destroy to self destruct MESA_DIR after successful test
|
168
|
+
submission. Essentially does rm -rf MESA_DIR after the test suite. Useful
|
169
|
+
for automated testing without piling up disk space.
|
170
|
+
|
171
|
+
Use flag --force to skip confirmation of computer details if they can be
|
172
|
+
read from ~/.mesa_test.yml.
|
173
|
+
LONGDESC
|
174
|
+
option :destroy, type: :boolean
|
175
|
+
option :force, type: :boolean
|
176
|
+
def install_and_test_all(version, mesa_dir)
|
177
|
+
s = MesaTestSubmitter.new_from_config
|
178
|
+
unless options[:force]
|
179
|
+
unless s.confirm_computer_data
|
180
|
+
s.setup
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
m = Mesa.download(version_number:version, new_mesa_dir: mesa_dir)
|
185
|
+
m.clean
|
186
|
+
m.install
|
187
|
+
|
188
|
+
raise MesaDirError, "Invalid MESA_DIR: #{mesa_dir}. Please download and " +
|
189
|
+
"install a valid MESA version or provide the path to one." unless
|
190
|
+
m.installed?
|
191
|
+
m.load_test_source_data
|
192
|
+
|
193
|
+
# run all tests
|
194
|
+
m.each_test_run_and_diff
|
195
|
+
|
196
|
+
# submit all tests
|
197
|
+
successfully_submitted = s.submit_all(m)
|
198
|
+
|
199
|
+
# if requested, and if submission successful, destroy the directory
|
200
|
+
if successfully_submitted and options[:destroy]
|
201
|
+
m.destroy
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
private
|
206
|
+
def check_user_and_computer(submitter)
|
207
|
+
computer_check = submitter.confirm_computer
|
208
|
+
if computer_check['valid']
|
209
|
+
puts computer_check['message']
|
210
|
+
else
|
211
|
+
$stderr.puts computer_check['message']
|
212
|
+
exit 1
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
# actually start the CLI
|
221
|
+
MesaTest.start(ARGV)
|
data/lib/mesa_test.rb
ADDED
@@ -0,0 +1,996 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'socket'
|
3
|
+
require 'os'
|
4
|
+
require 'yaml'
|
5
|
+
require 'uri'
|
6
|
+
require 'net/http'
|
7
|
+
require 'net/https'
|
8
|
+
require 'thor'
|
9
|
+
require 'json'
|
10
|
+
|
11
|
+
class MesaDirError < StandardError; end
|
12
|
+
class TestCaseDirError < StandardError; end
|
13
|
+
class InvalidDataType < StandardError; end
|
14
|
+
|
15
|
+
class MesaTestSubmitter
|
16
|
+
|
17
|
+
# set up config file for computer
|
18
|
+
def setup
|
19
|
+
update do |s|
|
20
|
+
puts "This wizard will guide you through setting up a computer profile
|
21
|
+
and default data for test case submissions to MESATestHub. You
|
22
|
+
will be able to confirm entries at the end. Default/current values are always
|
23
|
+
shown in parentheses at the end of a prompt. Pressing enter will accept the
|
24
|
+
default values.
|
25
|
+
|
26
|
+
To submit to MESATestHub, a valid computer name, email address, and password
|
27
|
+
are all required. All other data are useful, but optional. Any data
|
28
|
+
transferred to MESATestHub will be encrypted via HTTPS, but be warned that your
|
29
|
+
e-mail and password will be stored in plain text.
|
30
|
+
"
|
31
|
+
# Get computer name
|
32
|
+
response = shell.ask("What is the name of this computer (required)? "+
|
33
|
+
"(#{s.computer_name}):", color = :blue)
|
34
|
+
s.computer_name = response unless response.empty?
|
35
|
+
|
36
|
+
# Get user name
|
37
|
+
response = shell.ask "What is the name of the operator of this " +
|
38
|
+
"computer? (#{s.user_name}):", color = :blue
|
39
|
+
s.user_name = response unless response.empty?
|
40
|
+
|
41
|
+
# Get user e-mail
|
42
|
+
response = shell.ask "What is the email you can be reached " +
|
43
|
+
"at (required)? (#{s.email}):", color = :blue
|
44
|
+
s.email = response unless response.empty?
|
45
|
+
|
46
|
+
# Get user password
|
47
|
+
response = shell.ask "What is the password associated with the email " +
|
48
|
+
"#{s.email} (required)? (#{s.password})", color = :blue
|
49
|
+
s.password = response unless response.empty?
|
50
|
+
|
51
|
+
# Get platform information
|
52
|
+
response = shell.ask "What is the platform of this computer (eg. " +
|
53
|
+
"macOS, Ubuntu)? (#{s.platform}):", color = :blue
|
54
|
+
s.platform = response unless response.empty?
|
55
|
+
response = shell.ask "What is the version of the platform (eg. 10.13, "+
|
56
|
+
"16.04)? (#{s.platform_version}):", color = :blue
|
57
|
+
s.platform_version = response unless response.empty?
|
58
|
+
|
59
|
+
# Get processor information
|
60
|
+
response = shell.ask "What type of processor does this computer have " +
|
61
|
+
"(eg. 3.1 GHz Intel i7)? (#{s.processor}):", color = :blue
|
62
|
+
s.processor = response unless response.empty?
|
63
|
+
|
64
|
+
# Get ram information
|
65
|
+
response = shell.ask "How much RAM (in integer GB) does this computer " +
|
66
|
+
"have (eg. 8)? (#{s.ram_gb}) ", color = :blue
|
67
|
+
s.ram_gb = response.to_i unless response.empty?
|
68
|
+
|
69
|
+
# Get compiler information
|
70
|
+
response = shell.ask "Which compiler are you using? (#{s.compiler}):",
|
71
|
+
color = :blue, limited_to: ['', 'SDK', 'gfortran', 'ifort']
|
72
|
+
s.compiler = response unless response.empty?
|
73
|
+
|
74
|
+
# Get compiler version
|
75
|
+
response = shell.ask "What version of the compiler (eg. 20170921 or " +
|
76
|
+
"7.2.0)? (#{s.compiler_version}): ", color = :blue
|
77
|
+
s.compiler_version = response unless response.empty?
|
78
|
+
|
79
|
+
# Confirm save location
|
80
|
+
response = shell.ask "This will be saved in #{s.config_file}. Press " +
|
81
|
+
"enter to accept or enter a new location:", color = :blue, path: true
|
82
|
+
s.config_file = response unless response.empty?
|
83
|
+
end
|
84
|
+
|
85
|
+
# Confirm data. If not confirmed, restart whole wizard.
|
86
|
+
if confirm_computer_data
|
87
|
+
save_computer_data
|
88
|
+
else
|
89
|
+
puts "Restarting wizard.\n"
|
90
|
+
setup
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.new_from_config(
|
95
|
+
config_file: File.join(ENV['HOME'], '.mesa_test.yml'), force_setup: false,
|
96
|
+
base_uri: 'https://mesa-test-hub.herokuapp.com')
|
97
|
+
new_submitter = self.new(config_file: config_file, base_uri: base_uri)
|
98
|
+
if force_setup
|
99
|
+
new_submitter.setup
|
100
|
+
elsif not File.exist? config_file
|
101
|
+
puts "No such config file #{config_file}. Starting setup wizard."
|
102
|
+
new_submitter.setup
|
103
|
+
end
|
104
|
+
new_submitter.load_computer_data
|
105
|
+
return new_submitter
|
106
|
+
end
|
107
|
+
|
108
|
+
attr_accessor :computer_name, :user_name, :email, :password, :platform,
|
109
|
+
:platform_version, :processor, :ram_gb, :compiler, :compiler_version,
|
110
|
+
:config_file, :base_uri
|
111
|
+
|
112
|
+
attr_reader :shell
|
113
|
+
|
114
|
+
# many defaults are set in body
|
115
|
+
def initialize(computer_name: nil, user_name: nil, email: nil, platform: nil,
|
116
|
+
platform_version: nil, processor: nil, ram_gb: nil, compiler: nil,
|
117
|
+
compiler_version: nil, config_file: nil, base_uri: nil)
|
118
|
+
@computer_name = computer_name || Socket.gethostname.scan(/^[^\.]+\.?/)[0]
|
119
|
+
@computer_name.chomp!('.') if @computer_name
|
120
|
+
@user_name = user_name || (ENV['USER'] || ENV['USERNAME'])
|
121
|
+
@email = email || ''
|
122
|
+
@password = password || ''
|
123
|
+
@platform = platform
|
124
|
+
if @platform.nil?
|
125
|
+
@platform = if OS.osx?
|
126
|
+
'macOS'
|
127
|
+
elsif OS.linux?
|
128
|
+
'Linux'
|
129
|
+
else
|
130
|
+
''
|
131
|
+
end
|
132
|
+
end
|
133
|
+
@platform_version = platform_version || ''
|
134
|
+
@processor = processor || ''
|
135
|
+
@ram_gb = ram_gb || 0
|
136
|
+
@compiler = compiler || 'SDK'
|
137
|
+
@compiler_version = compiler_version || ''
|
138
|
+
@config_file = config_file || File.join(ENV['HOME'], '.mesa_test.yml')
|
139
|
+
@base_uri = base_uri
|
140
|
+
|
141
|
+
# set up thor-proof way to get responses from user. Thor hijacks the
|
142
|
+
# gets command, so we have to use its built-in "ask" method, which is
|
143
|
+
# actually more useful
|
144
|
+
@shell = Thor::Shell::Color.new
|
145
|
+
yield self if block_given?
|
146
|
+
end
|
147
|
+
|
148
|
+
# ease setup of a blank/default submitter
|
149
|
+
def update
|
150
|
+
yield self if block_given?
|
151
|
+
end
|
152
|
+
|
153
|
+
def confirm_computer_data
|
154
|
+
puts "Ready to submit the following data:"
|
155
|
+
puts "-------------------------------------------------------"
|
156
|
+
puts "Computer Name #{computer_name}"
|
157
|
+
puts "User Name #{user_name}"
|
158
|
+
puts "User email #{email}"
|
159
|
+
puts "Password ***********"
|
160
|
+
puts "Platform #{platform} #{platform_version}"
|
161
|
+
puts "Processor #{processor}"
|
162
|
+
puts "RAM #{ram_gb} GB"
|
163
|
+
puts "Compiler #{compiler} #{compiler_version}"
|
164
|
+
puts "Config location #{config_file}"
|
165
|
+
puts "-------------------------------------------------------"
|
166
|
+
puts ""
|
167
|
+
shell = Thor.new
|
168
|
+
response = shell.ask "Is this correct? (y/Y = Yes, anything else = No):"
|
169
|
+
if response.strip.downcase == 'y'
|
170
|
+
return true
|
171
|
+
else
|
172
|
+
return false
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# For one "computer" on the web server, and for [subjective] consistency
|
177
|
+
# reasons, the platform, processor, and RAM cannot be changed! If you
|
178
|
+
# change platforms (i.e. switch from mac to linux, or change between linux
|
179
|
+
# flavors), you should create a new computer account. Similarly, create new
|
180
|
+
# computer accounts if you change your RAM or processor. You do not need
|
181
|
+
# to change computers if you upgrade your platform (macOS 10.12 -> 10.13) or
|
182
|
+
# if you try different compilers
|
183
|
+
#
|
184
|
+
# Note this is NOT checked! The server really only uses the test-by-test
|
185
|
+
# quantities (platform version, compiler, complier version) and the
|
186
|
+
# computer name. Once the computer is found (by the name) all the other
|
187
|
+
# data is assumed to be fixed. The others... probably shouldn't be here,
|
188
|
+
# but remain so you can confirm that the computer on the web server is the
|
189
|
+
# same one you think you are working with locally.
|
190
|
+
def save_computer_data
|
191
|
+
data_hash = {
|
192
|
+
computer_name: computer_name,
|
193
|
+
user_name: user_name,
|
194
|
+
email: email,
|
195
|
+
password: password,
|
196
|
+
platform: platform,
|
197
|
+
processor: processor,
|
198
|
+
ram_gb: ram_gb,
|
199
|
+
platform_version: platform_version,
|
200
|
+
compiler: compiler,
|
201
|
+
compiler_version: compiler_version
|
202
|
+
}
|
203
|
+
File.open(config_file, 'w') { |f| f.write(YAML.dump(data_hash))}
|
204
|
+
end
|
205
|
+
|
206
|
+
def load_computer_data
|
207
|
+
data_hash = YAML::load(File.read(config_file))
|
208
|
+
@computer_name = data_hash[:computer_name]
|
209
|
+
@user_name = data_hash[:user_name]
|
210
|
+
@email = data_hash[:email]
|
211
|
+
@password = data_hash[:password]
|
212
|
+
@platform = data_hash[:platform]
|
213
|
+
@processor = data_hash[:processor]
|
214
|
+
@ram_gb = data_hash[:ram_gb]
|
215
|
+
@platform_version = data_hash[:platform_version]
|
216
|
+
@compiler = data_hash[:compiler]
|
217
|
+
@compiler_version = data_hash[:compiler_version]
|
218
|
+
end
|
219
|
+
|
220
|
+
# create and return hash of parameters for a TestInstance submission
|
221
|
+
def submit_params(test_case)
|
222
|
+
res = {
|
223
|
+
test_case: test_case.test_name,
|
224
|
+
computer: computer_name,
|
225
|
+
email: email,
|
226
|
+
password: password,
|
227
|
+
runtime_seconds: test_case.runtime_seconds,
|
228
|
+
mesa_version: test_case.mesa_version,
|
229
|
+
passed: test_case.passed?,
|
230
|
+
compiler: compiler,
|
231
|
+
compiler_version: compiler_version,
|
232
|
+
omp_num_threads: test_case.test_omp_num_threads,
|
233
|
+
}
|
234
|
+
|
235
|
+
# enter in test-specific data
|
236
|
+
test_case.data_names.each do |data_name|
|
237
|
+
unless test_case.data[data_name].nil?
|
238
|
+
res[data_name] = test_case.data[data_name]
|
239
|
+
end
|
240
|
+
end
|
241
|
+
res
|
242
|
+
end
|
243
|
+
|
244
|
+
def confirm_computer
|
245
|
+
uri = URI.parse(base_uri + '/check_computer.json')
|
246
|
+
https = Net::HTTP.new(uri.hostname, uri.port)
|
247
|
+
https.use_ssl = true if base_uri.include? 'https'
|
248
|
+
|
249
|
+
request = Net::HTTP::Post.new(uri,
|
250
|
+
initheader = { 'Content-Type' => 'application/json' })
|
251
|
+
request.body = {
|
252
|
+
email: email,
|
253
|
+
password: password,
|
254
|
+
computer_name: computer_name
|
255
|
+
}.to_json
|
256
|
+
|
257
|
+
JSON.load(https.request(request).body).to_hash
|
258
|
+
end
|
259
|
+
|
260
|
+
# attempt to post to MesaTestHub with test_case parameters
|
261
|
+
# returns true if the id is in the returned JSON (indicating success)
|
262
|
+
# otherwise returns false (maybe failed in authorization or in finding
|
263
|
+
# computer or test case) No error thrown for failure, though.
|
264
|
+
def submit(test_case, verbose=false)
|
265
|
+
uri = URI.parse(base_uri + '/test_instances/submit.json')
|
266
|
+
https = Net::HTTP.new(uri.hostname, uri.port)
|
267
|
+
https.use_ssl = true if base_uri.include? 'https'
|
268
|
+
|
269
|
+
request = Net::HTTP::Post.new(uri,
|
270
|
+
initheader = {'Content-Type' => 'application/json'})
|
271
|
+
request.body = submit_params(test_case).to_json
|
272
|
+
|
273
|
+
response = https.request request
|
274
|
+
puts JSON.load(response.body).to_hash if verbose
|
275
|
+
not response.kind_of? Net::HTTPUnprocessableEntity
|
276
|
+
end
|
277
|
+
|
278
|
+
def submit_all(mesa)
|
279
|
+
uri=URI.parse(base_uri + '/test_instances/submit.json')
|
280
|
+
submitted_cases = []
|
281
|
+
unsubmitted_cases = []
|
282
|
+
mesa.test_names.each do |mod, test_names|
|
283
|
+
test_names.each do |test_name|
|
284
|
+
# get at test case
|
285
|
+
test_case = mesa.test_cases[mod][test_name]
|
286
|
+
# try to submit and note if it does or doesn't successfully submit
|
287
|
+
submitted = false
|
288
|
+
unless test_case.outcome == :not_tested
|
289
|
+
submitted = submit(test_case)
|
290
|
+
end
|
291
|
+
|
292
|
+
if submitted
|
293
|
+
submitted_cases << test_name
|
294
|
+
else
|
295
|
+
unsubmitted_cases << test_name
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
puts ''
|
300
|
+
if submitted_cases.length > 0
|
301
|
+
shell.say "Submitted the following cases:", color = :green
|
302
|
+
puts submitted_cases.join("\n")
|
303
|
+
else
|
304
|
+
shell.say "Did not successfully submit any cases.", color = :red
|
305
|
+
end
|
306
|
+
if unsubmitted_cases.length > 0
|
307
|
+
puts "\n\n\n"
|
308
|
+
shell.say "Failed to submit the following cases:", color = :red
|
309
|
+
puts unsubmitted_cases.join("\n")
|
310
|
+
end
|
311
|
+
# return true if all cases were submitted
|
312
|
+
submitted_cases.length == mesa.test_names.length
|
313
|
+
end
|
314
|
+
|
315
|
+
end
|
316
|
+
|
317
|
+
|
318
|
+
class Mesa
|
319
|
+
attr_reader :mesa_dir, :test_data, :test_names, :test_cases, :shell
|
320
|
+
|
321
|
+
def self.download(version_number: nil, new_mesa_dir: nil)
|
322
|
+
new_mesa_dir ||= File.join(ENV['HOME'], 'mesa-test-r' + version_number.to_s)
|
323
|
+
success = system("svn co -r #{version_number} "+
|
324
|
+
"svn://svn.code.sf.net/p/mesa/code/trunk #{new_mesa_dir}")
|
325
|
+
unless success
|
326
|
+
raise MesaDirError, "Encountered a problem in downlaod mesa " +
|
327
|
+
"revision #{version_number}."
|
328
|
+
end
|
329
|
+
Mesa.new(mesa_dir: new_mesa_dir)
|
330
|
+
end
|
331
|
+
|
332
|
+
def initialize(mesa_dir: ENV['MESA_DIR'])
|
333
|
+
@mesa_dir = mesa_dir
|
334
|
+
|
335
|
+
# these get populated by calling #load_test_data
|
336
|
+
@test_data = {}
|
337
|
+
@test_names = {}
|
338
|
+
@test_cases = {}
|
339
|
+
|
340
|
+
# way to output colored text
|
341
|
+
@shell = Thor::Shell::Color.new
|
342
|
+
end
|
343
|
+
|
344
|
+
# read version number from $MESA_DIR/data/version_number
|
345
|
+
def version_number
|
346
|
+
contents = ''
|
347
|
+
File.open(File.join(mesa_dir, 'data', 'version_number'), 'r') do |f|
|
348
|
+
contents = f.read
|
349
|
+
end
|
350
|
+
contents.strip.to_i
|
351
|
+
end
|
352
|
+
|
353
|
+
def clean
|
354
|
+
visit_and_check mesa_dir, MesaDirError, "Encountered a problem in " +
|
355
|
+
"running `clean` in #{mesa_dir}." do
|
356
|
+
puts './clean'
|
357
|
+
system('./clean')
|
358
|
+
end
|
359
|
+
self
|
360
|
+
end
|
361
|
+
|
362
|
+
def install
|
363
|
+
visit_and_check mesa_dir, MesaDirError, "Encountered a problem in " +
|
364
|
+
"running `install` in #{mesa_dir}." do
|
365
|
+
puts './install'
|
366
|
+
system('./install')
|
367
|
+
end
|
368
|
+
self
|
369
|
+
end
|
370
|
+
|
371
|
+
def destroy
|
372
|
+
FileUtils.rm_rf mesa_dir
|
373
|
+
end
|
374
|
+
|
375
|
+
|
376
|
+
## TEST SUITE METHODS
|
377
|
+
|
378
|
+
def check_mod(mod)
|
379
|
+
unless MesaTestCase.modules.include? mod
|
380
|
+
raise TestCaseDirError, "Invalid module: #{mod}. Must be one of: " +
|
381
|
+
MesaTestCase.modules.join(', ')
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
def test_suite_dir(mod: nil)
|
386
|
+
check_mod mod
|
387
|
+
File.join(mesa_dir, mod.to_s, 'test_suite')
|
388
|
+
end
|
389
|
+
|
390
|
+
# load data from the `do1_test_source` file that gets used in a lot of
|
391
|
+
# testing
|
392
|
+
def load_test_source_data(mod: :all)
|
393
|
+
# allow for brainless loading of all module data
|
394
|
+
if mod == :all
|
395
|
+
MesaTestCase.modules.each do |this_mod|
|
396
|
+
load_test_source_data(mod: this_mod)
|
397
|
+
end
|
398
|
+
else
|
399
|
+
check_mod mod
|
400
|
+
# load data from the source file
|
401
|
+
source_lines = IO.readlines(File.join(test_suite_dir(mod: mod),
|
402
|
+
'do1_test_source'))
|
403
|
+
|
404
|
+
# initialize data hash to empty hash and name array to empty array
|
405
|
+
@test_data[mod] = {}
|
406
|
+
@test_names[mod] = []
|
407
|
+
@test_cases[mod] = {}
|
408
|
+
|
409
|
+
# read through each line and find four data, name, success string, final
|
410
|
+
# model name, and photo. Either of model name and photo can be "skip"
|
411
|
+
source_lines.each do |line|
|
412
|
+
no_skip = /^do_one (.+) "([^"]*)" "([^"]+)" (x?\d+)/
|
413
|
+
one_skip = /^do_one (.+) "([^"]*)" "([^"]+)" skip/
|
414
|
+
two_skip = /^do_one (.+) "([^"]*)" skip skip/
|
415
|
+
found_test = false
|
416
|
+
if line =~ no_skip
|
417
|
+
found_test = true
|
418
|
+
@test_data[mod][$1] = {success_string: $2, final_model: $3,
|
419
|
+
photo: $4}
|
420
|
+
elsif line =~ one_skip
|
421
|
+
found_test = true
|
422
|
+
@test_data[mod][$1] = {success_string: $2, final_model: $3,
|
423
|
+
photo: nil}
|
424
|
+
elsif line =~ two_skip
|
425
|
+
found_test = true
|
426
|
+
@test_data[mod][$1] = {success_string: $2, final_model: nil,
|
427
|
+
photo: nil}
|
428
|
+
end
|
429
|
+
|
430
|
+
if found_test
|
431
|
+
@test_names[mod] << $1 unless @test_names.include? $1
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
# make MesaTestCase objects accessible by name
|
436
|
+
@test_names[mod].each do |test_name|
|
437
|
+
data = @test_data[mod][test_name]
|
438
|
+
@test_cases[mod][test_name] = MesaTestCase.new(test: test_name,
|
439
|
+
mesa: self, success_string: data[:success_string], mod: mod,
|
440
|
+
final_model: data[:final_model], photo: data[:photo])
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
def find_test_case(test_case_name: nil, mod: :all)
|
446
|
+
if mod == :all
|
447
|
+
# look through all loaded modules for desired test case name, return
|
448
|
+
# FIRST found (assuming no name duplication across modules)
|
449
|
+
@test_names.each do |mod, names|
|
450
|
+
if names.include? test_case_name
|
451
|
+
return @test_cases[mod][test_case_name]
|
452
|
+
end
|
453
|
+
end
|
454
|
+
# didn't find any matches, return nil
|
455
|
+
return nil
|
456
|
+
else
|
457
|
+
# module specified; check it and return the proper test case (may be nil
|
458
|
+
# if the test case doesn't exist)
|
459
|
+
check_mod mod
|
460
|
+
@test_cases[mod][test_case_name]
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
# based off of `$MESA_DIR/star/test_suite/each_test_run_and_diff` from
|
465
|
+
# revision 10000
|
466
|
+
def each_test_clean(mod: :all)
|
467
|
+
if mod == :all
|
468
|
+
MesaTestCase.modules.each { |this_mod| each_test_clean mod: this_mod }
|
469
|
+
else
|
470
|
+
check_mod mod
|
471
|
+
test_names[mod].each do |test_name|
|
472
|
+
test_cases[mod][test_name].clean
|
473
|
+
end
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
def each_test_run_and_diff(mod: :all, log_results: false)
|
478
|
+
each_test_clean(mod: mod)
|
479
|
+
|
480
|
+
if mod == :all
|
481
|
+
MesaTestCase.modules.each do
|
482
|
+
|this_mod| each_test_run_and_diff(mod: this_mod,
|
483
|
+
log_results: log_results)
|
484
|
+
end
|
485
|
+
else
|
486
|
+
test_names[mod].each do |test_name|
|
487
|
+
test_cases[mod][test_name].do_one
|
488
|
+
test_cases[mod][test_name].log_results if log_results
|
489
|
+
end
|
490
|
+
log_summary if log_results
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
# note that this only changes MESA_DIR for subprocesses launched from ruby
|
495
|
+
# the old value of MESA_DIR will persist after the ruby process ends
|
496
|
+
def set_mesa_dir
|
497
|
+
ENV['MESA_DIR'] = mesa_dir
|
498
|
+
end
|
499
|
+
|
500
|
+
def installed?
|
501
|
+
check_mesa_dir
|
502
|
+
end
|
503
|
+
|
504
|
+
private
|
505
|
+
|
506
|
+
# verify that mesa_dir is valid by checking for version number and test_suite
|
507
|
+
# directory
|
508
|
+
def check_mesa_dir
|
509
|
+
res = File.exist?(File.join(mesa_dir, 'data', 'version_number'))
|
510
|
+
MesaTestCase.modules.each do |mod|
|
511
|
+
res = res and File.directory? test_suite_dir(mod: mod)
|
512
|
+
end
|
513
|
+
res
|
514
|
+
end
|
515
|
+
|
516
|
+
def log_summary(mod: :all)
|
517
|
+
if mod == :all
|
518
|
+
MesaTestCase.modules.each do |this_mod|
|
519
|
+
log_summary(mod: this_mod)
|
520
|
+
end
|
521
|
+
else
|
522
|
+
check_mod mod
|
523
|
+
res = []
|
524
|
+
test_names[mod].each do |test_name|
|
525
|
+
test_case = test_cases[mod][test_name]
|
526
|
+
res << {
|
527
|
+
test_name: test_case.test_name,
|
528
|
+
outcome: test_case.outcome,
|
529
|
+
failure_type: test_case.failure_type,
|
530
|
+
success_type: test_case.success_type,
|
531
|
+
runtime_seconds: test_case.runtime_seconds,
|
532
|
+
omp_num_threads: test_case.test_omp_num_threads,
|
533
|
+
mesa_version: test_case.mesa_version
|
534
|
+
}
|
535
|
+
end
|
536
|
+
summary_file = File.join(test_suite_dir(mod: mod), 'test_summary.yml')
|
537
|
+
File.open(summary_file, 'w') do |f|
|
538
|
+
f.write(YAML::dump(res))
|
539
|
+
end
|
540
|
+
end
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
|
545
|
+
class MesaTestCase
|
546
|
+
attr_reader :test_name, :mesa_dir, :mesa, :success_string, :final_model,
|
547
|
+
:failure_msg, :success_msg, :photo, :runtime_seconds,
|
548
|
+
:test_omp_num_threads, :mesa_version, :shell
|
549
|
+
attr_accessor :data_names, :data_types, :failure_type, :success_type,
|
550
|
+
:outcome
|
551
|
+
|
552
|
+
def self.modules
|
553
|
+
[:star, :binary]
|
554
|
+
end
|
555
|
+
|
556
|
+
def initialize(test: nil, mesa: nil, success_string: '',
|
557
|
+
final_model: 'final.mod', photo: nil, mod: nil)
|
558
|
+
@test_name = test
|
559
|
+
@mesa_dir = mesa.mesa_dir
|
560
|
+
@mesa = mesa
|
561
|
+
@mesa_version = mesa.version_number
|
562
|
+
@success_string = success_string
|
563
|
+
@final_model = final_model
|
564
|
+
@photo = photo
|
565
|
+
@failure_type = nil
|
566
|
+
@success_type = nil
|
567
|
+
@outcome = :not_tested
|
568
|
+
@runtime_seconds = 0
|
569
|
+
@test_omp_num_threads = 1
|
570
|
+
unless MesaTestCase.modules.include? mod
|
571
|
+
raise TestCaseDirError, "Invalid module: #{mod}. Must be one of: " +
|
572
|
+
MesaTestCase.modules.join(', ')
|
573
|
+
end
|
574
|
+
@mod = mod
|
575
|
+
@failure_msg = {
|
576
|
+
run_test_string: "#{test_name} failed: does not match test string",
|
577
|
+
run_checksum: "#{test_name} run failed: checksum for #{final_model} " +
|
578
|
+
"does not match after ./rn",
|
579
|
+
run_diff: "#{test_name} run failed: diff #{final_model} " +
|
580
|
+
"final_check.mod after ./rn",
|
581
|
+
photo_file: "#{test_name} restart failed: #{photo} does not exist",
|
582
|
+
photo_checksum: "#{test_name} restart failed: checksum for " +
|
583
|
+
"#{final_model} does not match after ./re",
|
584
|
+
photo_diff: "#{test_name} restart failed: diff #{final_model} " +
|
585
|
+
"final_check.mod after ./re"
|
586
|
+
}
|
587
|
+
@success_msg = {
|
588
|
+
run_test_string: "#{test_name} run: found test string: " +
|
589
|
+
"'#{success_string}'",
|
590
|
+
run_checksum: "#{test_name} run: checksum for #{final_model} matches " +
|
591
|
+
"after ./rn",
|
592
|
+
photo_checksum: "#{test_name} restart: checksum for #{final_model} " +
|
593
|
+
"matches after ./re #{photo}"
|
594
|
+
}
|
595
|
+
|
596
|
+
|
597
|
+
# validate stuff
|
598
|
+
check_mesa_dir
|
599
|
+
check_test_case
|
600
|
+
|
601
|
+
@data = {}
|
602
|
+
@data_names = []
|
603
|
+
|
604
|
+
# way to output colored text to shell
|
605
|
+
@shell = Thor::Shell::Color.new
|
606
|
+
end
|
607
|
+
|
608
|
+
def passed?
|
609
|
+
if @outcome == :pass
|
610
|
+
true
|
611
|
+
elsif @outcome == :fail
|
612
|
+
false
|
613
|
+
else
|
614
|
+
raise TestCaseDirError, "Cannot determine pass/fail status of " +
|
615
|
+
"#{test_name} yet."
|
616
|
+
end
|
617
|
+
end
|
618
|
+
|
619
|
+
def test_suite_dir
|
620
|
+
mesa.test_suite_dir(mod: @mod)
|
621
|
+
end
|
622
|
+
|
623
|
+
def test_case_dir
|
624
|
+
File.join(test_suite_dir, test_name)
|
625
|
+
end
|
626
|
+
|
627
|
+
def add_datum(datum_name, datum_type)
|
628
|
+
raise InvalidDataType, "Invalid data type: #{datum_type}. Must be one of "+
|
629
|
+
data_types.join(', ') + '.' unless data_types.include? datum_type.to_sym
|
630
|
+
@data[datum_name] = datum_type
|
631
|
+
@data_names << datum_name
|
632
|
+
end
|
633
|
+
|
634
|
+
def omp_num_threads
|
635
|
+
return ENV['OMP_NUM_THREADS'].to_i || 1
|
636
|
+
end
|
637
|
+
|
638
|
+
# based on $MESA_DIR/star/test_suite/each_test_clean, revision 10000
|
639
|
+
def clean
|
640
|
+
shell.say("cleaning #{test_name}", color = :blue)
|
641
|
+
puts ''
|
642
|
+
check_mesa_dir
|
643
|
+
check_test_case
|
644
|
+
in_dir do
|
645
|
+
puts "./clean"
|
646
|
+
unless system('./clean')
|
647
|
+
raise TestCaseDirError, "Encountered an error while running ./clean " +
|
648
|
+
"in #{Dir.getwd}."
|
649
|
+
end
|
650
|
+
shell.say "Removing all files from LOGS, LOGS1, LOGS2, photos, " +
|
651
|
+
"photos1, and photos2", color = :blue
|
652
|
+
FileUtils.rm_f Dir.glob('LOGS/*')
|
653
|
+
FileUtils.rm_f Dir.glob('LOGS1/*')
|
654
|
+
FileUtils.rm_f Dir.glob('LOGS2/*')
|
655
|
+
FileUtils.rm_f Dir.glob('photos/*')
|
656
|
+
FileUtils.rm_f Dir.glob('photos1/*')
|
657
|
+
FileUtils.rm_f Dir.glob('photos2/*')
|
658
|
+
|
659
|
+
shell.say "Removing files binary_history.data, out.txt, and " +
|
660
|
+
"test_results.yml", color = :blue
|
661
|
+
FileUtils.rm_f 'binary_history.data'
|
662
|
+
FileUtils.rm_f 'out.txt'
|
663
|
+
if File.directory? File.join('star_history','history_out')
|
664
|
+
shell.say "Removing all files of the form history_out* from star_history",
|
665
|
+
color = :blue
|
666
|
+
FileUtils.rm_f Dir.glob(File.join('star_history', 'history_out', '*'))
|
667
|
+
end
|
668
|
+
if File.directory? File.join('star_profile', 'profiles_out')
|
669
|
+
shell.say "Removing all files of the form profiles_out* from " +
|
670
|
+
"star_profile", color = :blue
|
671
|
+
FileUtils.rm_f Dir.glob(File.join('star_profile', 'profiles_out', '*'))
|
672
|
+
end
|
673
|
+
shell.say "Removing .running", color = :blue
|
674
|
+
FileUtils.rm_f '.running'
|
675
|
+
end
|
676
|
+
end
|
677
|
+
|
678
|
+
# based on $MESA_DIR/star/test_suite/each_test_run_and_diff, revision 10000
|
679
|
+
def do_one
|
680
|
+
@test_omp_num_threads = omp_num_threads
|
681
|
+
in_dir do
|
682
|
+
FileUtils.touch '.running'
|
683
|
+
shell.say("building and running #{test_name}", color = :blue)
|
684
|
+
puts ''
|
685
|
+
build_and_run
|
686
|
+
FileUtils.rm '.running'
|
687
|
+
puts ''
|
688
|
+
end
|
689
|
+
end
|
690
|
+
|
691
|
+
def log_results
|
692
|
+
# gets all parameters that would be submitted as well as computer
|
693
|
+
# information and dumps to a yml file in the test case directory
|
694
|
+
save_file = File.join(test_case_dir, 'test_results.yml')
|
695
|
+
shell.say "Logging test results to #{save_file}...", color = :blue
|
696
|
+
res = {
|
697
|
+
test_case: test_name,
|
698
|
+
runtime_seconds: runtime_seconds,
|
699
|
+
mesa_version: mesa_version,
|
700
|
+
outcome: outcome,
|
701
|
+
omp_num_threads: test_omp_num_threads,
|
702
|
+
success_type: success_type,
|
703
|
+
failure_type: failure_type
|
704
|
+
}
|
705
|
+
File.open(save_file, 'w') { |f| f.write(YAML::dump(res)) }
|
706
|
+
shell.say "Successfully saved results to file #{save_file}.",
|
707
|
+
color = :green
|
708
|
+
puts ''
|
709
|
+
end
|
710
|
+
|
711
|
+
def load_results
|
712
|
+
# loads all parameters from a previous test run, likely for submission
|
713
|
+
# purposes
|
714
|
+
load_file = File.join(test_case_dir, 'test_results.yml')
|
715
|
+
shell.say "Loading data from #{load_file}...", color = :blue
|
716
|
+
data = YAML::load(File.read(load_file))
|
717
|
+
@runtime_seconds = data[:runtime_seconds]
|
718
|
+
@mesa_version = data[:mesa_version]
|
719
|
+
@outcome = data[:outcome].to_sym
|
720
|
+
@test_omp_num_threads = data[:omp_num_threads]
|
721
|
+
@success_type = data[:success_type]
|
722
|
+
@failure_type = data[:failure_type]
|
723
|
+
shell.say "Done loading data from #{load_file}.", color = :green
|
724
|
+
puts ''
|
725
|
+
end
|
726
|
+
|
727
|
+
private
|
728
|
+
|
729
|
+
def data_types
|
730
|
+
return [:float, :integer, :string, :boolean]
|
731
|
+
end
|
732
|
+
|
733
|
+
# cd into the test case directory, do something in a block, then cd back
|
734
|
+
# to original directory
|
735
|
+
def in_dir(&block)
|
736
|
+
visit_dir(test_case_dir, &block)
|
737
|
+
end
|
738
|
+
|
739
|
+
# make sure that we can get to the test case directory. Throw an exception
|
740
|
+
# if we cannot
|
741
|
+
def check_test_case
|
742
|
+
unless File.directory? test_case_dir
|
743
|
+
raise TestCaseDirError, "No such test case: #{test_case_dir}."
|
744
|
+
end
|
745
|
+
end
|
746
|
+
|
747
|
+
# verify that mesa_dir is valid by checking for version number and test_suite
|
748
|
+
# directory
|
749
|
+
def check_mesa_dir
|
750
|
+
is_valid = File.exist?(File.join(mesa_dir, 'data', 'version_number')) and
|
751
|
+
File.directory?(test_suite_dir)
|
752
|
+
raise MesaDirError, "Invalid MESA dir: #{mesa_dir}" unless is_valid
|
753
|
+
end
|
754
|
+
|
755
|
+
# append message to log file
|
756
|
+
def log_message(msg, color = nil, log_file = 'out.txt')
|
757
|
+
if color.nil?
|
758
|
+
shell.say msg
|
759
|
+
else
|
760
|
+
shell.say msg, color = color
|
761
|
+
end
|
762
|
+
File.open('out.txt', 'a') { |f| f.puts(msg) }
|
763
|
+
end
|
764
|
+
|
765
|
+
# write failure message to log file
|
766
|
+
def write_failure_message
|
767
|
+
msg = "******************** #{failure_msg[@failure_type]} " +
|
768
|
+
"********************"
|
769
|
+
log_message(msg, color = :red)
|
770
|
+
end
|
771
|
+
|
772
|
+
# write success message to log file
|
773
|
+
def write_success_msg(success_type)
|
774
|
+
msg = 'PASS ' + success_msg[success_type]
|
775
|
+
log_message(msg, color = :green)
|
776
|
+
end
|
777
|
+
|
778
|
+
# used as return value for run or photo test. Logs failure to text file, and
|
779
|
+
# sets internal status to failing
|
780
|
+
def fail_test(failure_type)
|
781
|
+
@failure_type = failure_type
|
782
|
+
@outcome = :fail
|
783
|
+
write_failure_message
|
784
|
+
return false
|
785
|
+
end
|
786
|
+
|
787
|
+
# used as return value for run or photo test. Logs data to text file, and
|
788
|
+
# sets internal status to passing
|
789
|
+
def succeed(success_type)
|
790
|
+
@success_type = success_type
|
791
|
+
@outcome = :pass
|
792
|
+
write_success_msg(success_type)
|
793
|
+
return true
|
794
|
+
end
|
795
|
+
|
796
|
+
def check_run
|
797
|
+
# assumes we are in the directory already, called from something else
|
798
|
+
run_start = Time.now
|
799
|
+
|
800
|
+
# do the run
|
801
|
+
puts './rn >> out.txt 2> err.txt'
|
802
|
+
system('./rn >> out.txt 2> err.txt')
|
803
|
+
|
804
|
+
# report runtime and clean up
|
805
|
+
run_finish = Time.now
|
806
|
+
@runtime_seconds = (run_finish - run_start).to_i
|
807
|
+
shell.say "Finished with ./rn; runtime = #{@runtime_seconds} seconds.",
|
808
|
+
color = :blue
|
809
|
+
append_and_rm_err
|
810
|
+
|
811
|
+
|
812
|
+
# look for success text
|
813
|
+
success = true
|
814
|
+
File.open('out.txt', 'r') do |f|
|
815
|
+
success = !f.read.downcase.scan(success_string.downcase).empty?
|
816
|
+
end
|
817
|
+
# bail if there was no test string found
|
818
|
+
unless success
|
819
|
+
return fail_test(:run_test_string)
|
820
|
+
end
|
821
|
+
|
822
|
+
# additional checks for final model, if it is specified
|
823
|
+
if final_model
|
824
|
+
# update checks after new run (Bill W. doesn't know what this does)
|
825
|
+
# (is this supposed to mark things as passed? The original function
|
826
|
+
# just has a standard "return" statement, which I interpret as passing)
|
827
|
+
if ENV.include? 'UPDATE_CHECKS'
|
828
|
+
system("md5sum \"#{final_model}\" > checks.md5")
|
829
|
+
puts "md5sum \"#{final_model}\" > checks.md5"
|
830
|
+
FileUtils.cp final_model 'final_check.mod'
|
831
|
+
return true
|
832
|
+
end
|
833
|
+
|
834
|
+
# display runtime message
|
835
|
+
puts IO.readlines('out.txt').select { |line| line.scan(/runtime/i) }[-1]
|
836
|
+
|
837
|
+
# check that final model matches
|
838
|
+
puts './ck >& final_check_diff.txt'
|
839
|
+
if not system('./ck >& final_check_diff.txt')
|
840
|
+
return fail_test(:run_checksum)
|
841
|
+
elsif File.exist? 'final_check_diff.txt' and
|
842
|
+
not File.read('final_check_diff.txt').empty?
|
843
|
+
return fail_test(:run_diff)
|
844
|
+
elsif File.exist? final_model
|
845
|
+
return succeed(:run_checksum)
|
846
|
+
end
|
847
|
+
|
848
|
+
# no final model to check, and we already found the test string, so pass
|
849
|
+
else
|
850
|
+
return succeed(:run_test_string)
|
851
|
+
end
|
852
|
+
|
853
|
+
end
|
854
|
+
|
855
|
+
# prepare for and do restart, check results, and return pass/fail status
|
856
|
+
def check_restart
|
857
|
+
# abort if there is not photo specified
|
858
|
+
return unless photo
|
859
|
+
|
860
|
+
# check that photo file actually exists
|
861
|
+
unless File.exist?(File.join('photos', photo)) or
|
862
|
+
File.exist?(File.join('photos1', photo))
|
863
|
+
return fail_test(:photo_file)
|
864
|
+
end
|
865
|
+
|
866
|
+
# remove final model since it will be remade by restart
|
867
|
+
FileUtils.rm_f final_model
|
868
|
+
|
869
|
+
# do restart and consolidate output
|
870
|
+
puts "./re #{photo} >> out.txt 2> err.txt"
|
871
|
+
system("./re #{photo} >> out.txt 2> err.txt")
|
872
|
+
append_and_rm_err
|
873
|
+
|
874
|
+
# check that final model matches
|
875
|
+
puts "./ck >& final_check_diff.txt"
|
876
|
+
if not system("./ck >& final_check_diff.txt")
|
877
|
+
return fail_test(:photo_checksum)
|
878
|
+
elsif File.exist?('final_check_diff.txt') and not
|
879
|
+
File.read('final_check_diff.txt').empty?
|
880
|
+
return fail_test(:photo_diff)
|
881
|
+
else
|
882
|
+
return succeed(:photo_checksum)
|
883
|
+
end
|
884
|
+
end
|
885
|
+
|
886
|
+
def build_and_run
|
887
|
+
# assumes we are in the test case directory. Should only be called
|
888
|
+
# in the context of an `in_dir` block.
|
889
|
+
|
890
|
+
|
891
|
+
# first clean and make... worried about shell compatibility since we
|
892
|
+
# aren't forcing bash. Hopefully '>' for redirecting output is pretty
|
893
|
+
# universal
|
894
|
+
puts './clean'
|
895
|
+
unless system('./clean')
|
896
|
+
raise TestCaseDirError, "Encountered an error when running `clean` in " +
|
897
|
+
"#{Dir.getwd} for test case #{test_name}."
|
898
|
+
end
|
899
|
+
|
900
|
+
puts './mk > mk.txt'
|
901
|
+
unless system('./mk > mk.txt')
|
902
|
+
raise TestCaseDirError, "Encountered an error when running `mk` in " +
|
903
|
+
"#{Dir.getwd} for test case #{test_name}."
|
904
|
+
end
|
905
|
+
FileUtils.rm 'mk.txt'
|
906
|
+
|
907
|
+
# remove final model if it already exists
|
908
|
+
unless final_model.nil?
|
909
|
+
FileUtils.rm final_model if File.exist? final_model
|
910
|
+
end
|
911
|
+
|
912
|
+
if check_run
|
913
|
+
# only check restart/photo if we get through run successfully
|
914
|
+
check_restart
|
915
|
+
end
|
916
|
+
end
|
917
|
+
|
918
|
+
# append contents of err.txt to end of out.txt, then delete err.txt
|
919
|
+
def append_and_rm_err(outfile='out.txt', errfile='err.txt')
|
920
|
+
File.open(outfile, 'a') do |f_out|
|
921
|
+
err_contents = File.read(errfile)
|
922
|
+
unless err_contents.strip.empty?
|
923
|
+
shell.say "\nERRORS", color = :red
|
924
|
+
puts err_contents
|
925
|
+
shell.say "END OF ERRORS (appended to #{outfile})", color = :red
|
926
|
+
puts ''
|
927
|
+
f_out.write(err_contents)
|
928
|
+
end
|
929
|
+
end
|
930
|
+
FileUtils.rm errfile
|
931
|
+
end
|
932
|
+
|
933
|
+
end
|
934
|
+
|
935
|
+
|
936
|
+
################################
|
937
|
+
# GENERAL METHODS #
|
938
|
+
################################
|
939
|
+
|
940
|
+
|
941
|
+
# cd into a new directory, execute a block whose return value is either
|
942
|
+
# true or false. Either way, cd back to original directory. Raise an
|
943
|
+
# exception if the block failed (returned false or nil)
|
944
|
+
def visit_and_check(new_dir, exception, message)
|
945
|
+
cwd = Dir.getwd
|
946
|
+
shell.say "Leaving #{cwd}", color = :blue
|
947
|
+
puts ""
|
948
|
+
shell.say "Entering #{new_dir}.", color = :blue
|
949
|
+
Dir.chdir(new_dir)
|
950
|
+
success = yield if block_given?
|
951
|
+
shell.say "Leaving #{new_dir}", color = :blue
|
952
|
+
puts ""
|
953
|
+
shell.say "Entering #{cwd}.", color = :blue
|
954
|
+
Dir.chdir(cwd)
|
955
|
+
unless success
|
956
|
+
raise exception, message
|
957
|
+
end
|
958
|
+
end
|
959
|
+
|
960
|
+
# cd into a new directory, execute a block, then cd back into original
|
961
|
+
# directory
|
962
|
+
def visit_dir(new_dir)
|
963
|
+
cwd = Dir.getwd
|
964
|
+
shell.say "Leaving #{cwd}", color = :blue
|
965
|
+
puts ""
|
966
|
+
shell.say "Entering #{new_dir}.", color = :blue
|
967
|
+
Dir.chdir(new_dir)
|
968
|
+
yield if block_given?
|
969
|
+
shell.say "Leaving #{new_dir}", color = :blue
|
970
|
+
puts ""
|
971
|
+
shell.say "Entering #{cwd}.", color = :blue
|
972
|
+
Dir.chdir(cwd)
|
973
|
+
end
|
974
|
+
|
975
|
+
# create seed data for test cases for MesaTestHub of a given mesa version
|
976
|
+
def generate_seeds_rb(mesa_dir, outfile)
|
977
|
+
m = Mesa.new(mesa_dir: mesa_dir)
|
978
|
+
m.load_test_source_data
|
979
|
+
File.open(outfile, 'w') do |f|
|
980
|
+
f.puts 'test_cases = TestCase.create!('
|
981
|
+
f.puts ' ['
|
982
|
+
m.test_names.each do |test_case_name|
|
983
|
+
f.puts " {"
|
984
|
+
f.puts " name: '#{test_case_name}',"
|
985
|
+
f.puts " version_added: #{m.version_number},"
|
986
|
+
# no comma on last one
|
987
|
+
if test_case_name == m.test_names[-1]
|
988
|
+
f.puts " }"
|
989
|
+
else
|
990
|
+
f.puts " },"
|
991
|
+
end
|
992
|
+
end
|
993
|
+
f.puts ' ]'
|
994
|
+
f.puts ')'
|
995
|
+
end
|
996
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mesa_test
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- William Wolf
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-11-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: os
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: thor
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.19'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.19'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: json
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.0'
|
55
|
+
description: mesa_test is a command-line interface for running the test suites in
|
56
|
+
MESA and submitting them to the companion website MESATestHub.
|
57
|
+
email: wmwolf@asu.edu
|
58
|
+
executables:
|
59
|
+
- mesa_test
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- bin/mesa_test
|
64
|
+
- lib/mesa_test.rb
|
65
|
+
homepage: https://github.com/wmwolf/mesa_test
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata: {}
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.0.0
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 2.6.14
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: Command line tool for running and reporting the MESA test suites.
|
89
|
+
test_files: []
|