modsvaskr 0.1.11 → 0.1.12
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/modsvaskr +11 -11
- data/lib/modsvaskr/config.rb +78 -79
- data/lib/modsvaskr/encoding.rb +3 -1
- data/lib/modsvaskr/game.rb +208 -203
- data/lib/modsvaskr/games/skyrim_se.rb +106 -106
- data/lib/modsvaskr/in_game_tests_runner.rb +348 -348
- data/lib/modsvaskr/logger.rb +45 -44
- data/lib/modsvaskr/run_cmd.rb +23 -22
- data/lib/modsvaskr/tests_runner.rb +204 -205
- data/lib/modsvaskr/tests_suite.rb +70 -69
- data/lib/modsvaskr/tests_suites/exterior_cell.rb +120 -117
- data/lib/modsvaskr/tests_suites/interior_cell.rb +63 -63
- data/lib/modsvaskr/tests_suites/npc.rb +67 -64
- data/lib/modsvaskr/tests_suites/npc_head.rb +2 -1
- data/lib/modsvaskr/ui.rb +205 -204
- data/lib/modsvaskr/version.rb +5 -5
- data/lib/modsvaskr/xedit.rb +65 -65
- metadata +38 -10
@@ -1,64 +1,67 @@
|
|
1
|
-
require 'modsvaskr/in_game_tests_suite'
|
2
|
-
|
3
|
-
module Modsvaskr
|
4
|
-
|
5
|
-
module TestsSuites
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
#
|
32
|
-
# Array< [
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
#
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
1
|
+
require 'modsvaskr/in_game_tests_suite'
|
2
|
+
|
3
|
+
module Modsvaskr
|
4
|
+
|
5
|
+
module TestsSuites
|
6
|
+
|
7
|
+
# Test NPCs by taking screenshots
|
8
|
+
class Npc < TestsSuite
|
9
|
+
|
10
|
+
include InGameTestsSuite
|
11
|
+
|
12
|
+
# Return the in-game tests suite to which we forward the tests to be run
|
13
|
+
#
|
14
|
+
# Result::
|
15
|
+
# * Symbol: In-game tests suite
|
16
|
+
def in_game_tests_suite
|
17
|
+
:npcs
|
18
|
+
end
|
19
|
+
|
20
|
+
# Discover the list of tests information that could be run.
|
21
|
+
# [API] - This method is mandatory
|
22
|
+
#
|
23
|
+
# Result::
|
24
|
+
# * Hash< String, Hash<Symbol,Object> >: Ordered hash of test information, per test name
|
25
|
+
def discover_tests
|
26
|
+
tests = {}
|
27
|
+
@game.xedit.run_script('DumpInfo', only_once: true)
|
28
|
+
# Keep track of masters, per plugin
|
29
|
+
# Hash<String, Array<String> >
|
30
|
+
masters = {}
|
31
|
+
# Keep track of NPCs
|
32
|
+
# Array< [String, Integer, String] >
|
33
|
+
# Array< [Plugin, FormID, NPC ] >
|
34
|
+
npcs = []
|
35
|
+
@game.xedit.parse_csv('Modsvaskr_ExportedDumpInfo') do |row|
|
36
|
+
case row[1].downcase
|
37
|
+
when 'npc_'
|
38
|
+
npcs << [row[0].downcase, row[2].to_i(16), row[3]]
|
39
|
+
when 'tes4'
|
40
|
+
masters[row[0].downcase] = row[3..].map(&:downcase)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
npcs.each do |(esp, form_id, npc_name)|
|
44
|
+
raise "Esp #{esp} declares NPC FormID #{form_id} (#{npc_name}) but its masters could not be parsed" unless masters.key?(esp)
|
45
|
+
|
46
|
+
# Know from which mod this ID comes from
|
47
|
+
mod_idx = form_id / 16_777_216
|
48
|
+
raise "NPC FormID #{form_id} (#{npc_name}) from #{esp} references an unknown master (known masters: #{masters[esp].join(', ')})" if mod_idx > masters[esp].size
|
49
|
+
|
50
|
+
test_name = "#{mod_idx == masters[esp].size ? esp : masters[esp][mod_idx]}/#{form_id % 16_777_216}"
|
51
|
+
if tests.key?(test_name)
|
52
|
+
# Add the name of the mod to the description, so that we know which mod modifies which NPC.
|
53
|
+
tests[test_name][:name] << "/#{esp}"
|
54
|
+
else
|
55
|
+
tests[test_name] = {
|
56
|
+
name: "Take screenshot of #{npc_name} - #{esp}"
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
tests
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -4,6 +4,7 @@ module Modsvaskr
|
|
4
4
|
|
5
5
|
module TestsSuites
|
6
6
|
|
7
|
+
# Test NPCs by taking head screenshots
|
7
8
|
class NpcHead < TestsSuites::Npc
|
8
9
|
|
9
10
|
# Return the in-game tests suite to which we forward the tests to be run
|
@@ -21,7 +22,7 @@ module Modsvaskr
|
|
21
22
|
# * Hash< String, Hash<Symbol,Object> >: Ordered hash of test information, per test name
|
22
23
|
def discover_tests
|
23
24
|
tests = super
|
24
|
-
tests.
|
25
|
+
tests.each_value do |test_info|
|
25
26
|
test_info[:name].gsub!('Take screenshot', 'Take head screenshot')
|
26
27
|
end
|
27
28
|
tests
|
data/lib/modsvaskr/ui.rb
CHANGED
@@ -1,204 +1,205 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require 'modsvaskr/
|
4
|
-
require 'modsvaskr/
|
5
|
-
require 'modsvaskr/
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
'
|
74
|
-
|
75
|
-
'
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
end
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
end
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
125
|
-
|
126
|
-
end
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
)
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
end
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
end
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
end
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
logs_menu.item
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
1
|
+
require 'English'
|
2
|
+
require 'curses_menu'
|
3
|
+
require 'modsvaskr/logger'
|
4
|
+
require 'modsvaskr/tests_runner'
|
5
|
+
require 'modsvaskr/run_cmd'
|
6
|
+
require 'modsvaskr/version'
|
7
|
+
|
8
|
+
module Modsvaskr
|
9
|
+
|
10
|
+
# Main UI, using ncurses
|
11
|
+
class Ui
|
12
|
+
|
13
|
+
include RunCmd
|
14
|
+
include Logger
|
15
|
+
|
16
|
+
# Constructor
|
17
|
+
#
|
18
|
+
# Parameters::
|
19
|
+
# * *config* (Config): Configuration object
|
20
|
+
def initialize(config:)
|
21
|
+
log "Launch Modsvaskr UI v#{Modsvaskr::VERSION} - Logs in #{Logger.log_file}"
|
22
|
+
@config = config
|
23
|
+
end
|
24
|
+
|
25
|
+
# Run the UI
|
26
|
+
def run
|
27
|
+
last_modsvaskr_version = nil
|
28
|
+
gem_list_stdout = `gem list modsvaskr --remote`
|
29
|
+
gem_list_stdout.split("\n").each do |line|
|
30
|
+
if line =~ /^modsvaskr \((.+?)\)/
|
31
|
+
last_modsvaskr_version = Regexp.last_match(1)
|
32
|
+
break
|
33
|
+
end
|
34
|
+
end
|
35
|
+
log "!!! Could not get latest Modsvaskr version. Output of gem list modsvaskr --remote:\n#{gem_list_stdout}" if last_modsvaskr_version.nil?
|
36
|
+
key_presses = @config.auto_keys.map do |key_str|
|
37
|
+
case key_str
|
38
|
+
when 'KEY_ENTER', 'KEY_ESCAPE'
|
39
|
+
CursesMenu.const_get(key_str.to_sym)
|
40
|
+
when /^KEY_\w+$/
|
41
|
+
Curses.const_get(key_str.to_sym)
|
42
|
+
else
|
43
|
+
key_str
|
44
|
+
end
|
45
|
+
end
|
46
|
+
CursesMenu.new(
|
47
|
+
"Modsvaskr v#{Modsvaskr::VERSION} - Stronghold of Mods#{!last_modsvaskr_version.nil? && last_modsvaskr_version != Modsvaskr::VERSION ? " - !!! New version available: #{last_modsvaskr_version}" : ''}",
|
48
|
+
key_presses: key_presses
|
49
|
+
) do |main_menu|
|
50
|
+
@config.games.each do |game|
|
51
|
+
main_menu.item "#{game.name} (#{game.path})" do
|
52
|
+
CursesMenu.new(
|
53
|
+
"Modsvaskr v#{Modsvaskr::VERSION} - Stronghold of Mods > #{game.name}",
|
54
|
+
key_presses: key_presses
|
55
|
+
) do |game_menu|
|
56
|
+
game_menu.item 'Testing' do
|
57
|
+
# Read tests info
|
58
|
+
tests_runner = TestsRunner.new(@config, game)
|
59
|
+
# Selected test names, per test type
|
60
|
+
# Hash< Symbol, Hash< String, nil > >
|
61
|
+
selected_tests_suites = {}
|
62
|
+
CursesMenu.new(
|
63
|
+
"Modsvaskr v#{Modsvaskr::VERSION} - Stronghold of Mods > #{game.name} > Testing",
|
64
|
+
key_presses: key_presses
|
65
|
+
) do |test_menu|
|
66
|
+
tests_runner.tests_suites.each do |tests_suite|
|
67
|
+
statuses_for_suite = tests_runner.statuses_for(tests_suite)
|
68
|
+
all_tests_selected = selected_tests_suites.key?(tests_suite) &&
|
69
|
+
selected_tests_suites[tests_suite].keys.sort == statuses_for_suite.map { |(test_name, _test_status)| test_name }.sort
|
70
|
+
test_menu.item(
|
71
|
+
"[#{
|
72
|
+
if all_tests_selected
|
73
|
+
'*'
|
74
|
+
elsif selected_tests_suites.key?(tests_suite)
|
75
|
+
'+'
|
76
|
+
else
|
77
|
+
' '
|
78
|
+
end
|
79
|
+
}] #{tests_suite} - #{statuses_for_suite.select { |(_name, status)| status == 'ok' }.size} / #{statuses_for_suite.size}",
|
80
|
+
actions: {
|
81
|
+
'd' => {
|
82
|
+
name: 'Details',
|
83
|
+
execute: proc do
|
84
|
+
CursesMenu.new(
|
85
|
+
"Modsvaskr v#{Modsvaskr::VERSION} - Stronghold of Mods > #{game.name} > Testing > Tests #{tests_suite}",
|
86
|
+
key_presses: key_presses
|
87
|
+
) do |tests_suite_menu|
|
88
|
+
statuses_for_suite.each do |(test_name, test_status)|
|
89
|
+
test_selected = selected_tests_suites.key?(tests_suite) && selected_tests_suites[tests_suite].key?(test_name)
|
90
|
+
tests_suite_menu.item "[#{test_selected ? '*' : ' '}] #{test_name} - #{test_status} - #{tests_runner.test_info(tests_suite, test_name)[:name]}" do
|
91
|
+
if test_selected
|
92
|
+
selected_tests_suites[tests_suite].delete(test_name)
|
93
|
+
selected_tests_suites.delete(tests_suite) if selected_tests_suites[tests_suite].empty?
|
94
|
+
else
|
95
|
+
selected_tests_suites[tests_suite] = {} unless selected_tests_suites.key?(tests_suite)
|
96
|
+
selected_tests_suites[tests_suite][test_name] = nil
|
97
|
+
end
|
98
|
+
:menu_refresh
|
99
|
+
end
|
100
|
+
end
|
101
|
+
tests_suite_menu.item 'Back' do
|
102
|
+
:menu_exit
|
103
|
+
end
|
104
|
+
end
|
105
|
+
:menu_refresh
|
106
|
+
end
|
107
|
+
}
|
108
|
+
}
|
109
|
+
) do
|
110
|
+
if all_tests_selected
|
111
|
+
selected_tests_suites.delete(tests_suite)
|
112
|
+
else
|
113
|
+
selected_tests_suites[tests_suite] = statuses_for_suite.map { |(test_name, _test_status)| [test_name, nil] }.to_h
|
114
|
+
end
|
115
|
+
:menu_refresh
|
116
|
+
end
|
117
|
+
end
|
118
|
+
test_menu.item 'Select tests that are not ok' do
|
119
|
+
selected_tests_suites = {}
|
120
|
+
tests_runner.tests_suites.map do |tests_suite|
|
121
|
+
tests_not_ok = {}
|
122
|
+
tests_runner.statuses_for(tests_suite).each do |(test_name, test_status)|
|
123
|
+
tests_not_ok[test_name] = nil unless test_status == 'ok'
|
124
|
+
end
|
125
|
+
selected_tests_suites[tests_suite] = tests_not_ok unless tests_not_ok.empty?
|
126
|
+
end
|
127
|
+
:menu_refresh
|
128
|
+
end
|
129
|
+
test_menu.item 'Register tests from selected test suites' do
|
130
|
+
selected_tests_suites.each_key do |tests_suite|
|
131
|
+
tests_runner.set_statuses_for(
|
132
|
+
tests_suite,
|
133
|
+
(
|
134
|
+
tests_runner.discover_tests_for(tests_suite) -
|
135
|
+
tests_runner.statuses_for(tests_suite).map { |(test_name, _test_status)| test_name }
|
136
|
+
).map { |test_name| [test_name, ''] }
|
137
|
+
)
|
138
|
+
end
|
139
|
+
:menu_refresh
|
140
|
+
end
|
141
|
+
test_menu.item 'Unregister tests from selected test suites' do
|
142
|
+
selected_tests_suites.each_key do |tests_suite|
|
143
|
+
tests_runner.clear_tests_for(tests_suite)
|
144
|
+
end
|
145
|
+
:menu_refresh
|
146
|
+
end
|
147
|
+
test_menu.item 'Clear selected test statuses' do
|
148
|
+
selected_tests_suites.each do |tests_suite, test_names_set|
|
149
|
+
tests_runner.set_statuses_for(tests_suite, test_names_set.keys.map { |test_name| [test_name, ''] })
|
150
|
+
end
|
151
|
+
:menu_refresh
|
152
|
+
end
|
153
|
+
test_menu.item 'Run remaining selected tests' do
|
154
|
+
tests_runner.run(
|
155
|
+
selected_tests_suites.map do |selected_tests_suite, selected_test_names_set|
|
156
|
+
[
|
157
|
+
selected_tests_suite,
|
158
|
+
# Make sure tests to be run are ordered from the registered list
|
159
|
+
tests_runner.
|
160
|
+
statuses_for(selected_tests_suite).map { |(test_name, _test_status)| test_name }.
|
161
|
+
select { |test_name| selected_test_names_set.key?(test_name) }
|
162
|
+
]
|
163
|
+
end
|
164
|
+
)
|
165
|
+
:menu_refresh
|
166
|
+
end
|
167
|
+
test_menu.item 'Back' do
|
168
|
+
:menu_exit
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
game.complete_game_menu(game_menu) if game.respond_to?(:complete_game_menu)
|
173
|
+
game_menu.item 'Back' do
|
174
|
+
:menu_exit
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
main_menu.item 'See logs' do
|
180
|
+
CursesMenu.new(
|
181
|
+
'Modsvaskr - Stronghold of Mods > Logs',
|
182
|
+
key_presses: key_presses
|
183
|
+
) do |logs_menu|
|
184
|
+
File.read(Logger.log_file).split("\n").each do |line|
|
185
|
+
logs_menu.item line
|
186
|
+
end
|
187
|
+
logs_menu.item 'Back' do
|
188
|
+
:menu_exit
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
main_menu.item 'Quit' do
|
193
|
+
:menu_exit
|
194
|
+
end
|
195
|
+
end
|
196
|
+
rescue
|
197
|
+
log "Unhandled exception: #{$ERROR_INFO}\n#{$ERROR_INFO.backtrace.join("\n")}"
|
198
|
+
raise
|
199
|
+
ensure
|
200
|
+
log 'Close Modsvaskr UI'
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|