mod_organizer 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,292 @@
1
+ describe ModOrganizer do
2
+
3
+ shared_examples 'a ModOrganizer instance' do
4
+
5
+ context 'when validating ini file reading' do
6
+
7
+ it 'returns the game path' do
8
+ setup_mo(ini: { General: { gamePath: 'C:/path/to/Skyrim Special Edition' } })
9
+ expect(mod_organizer.game_path).to eq 'C:/path/to/Skyrim Special Edition'
10
+ end
11
+
12
+ it 'returns the game path from a ByteArray' do
13
+ setup_mo(ini: { General: { gamePath: '@ByteArray(C:\\\\path\\\\to\\\\Skyrim Special Edition)' } })
14
+ expect(mod_organizer.game_path).to eq 'C:/path/to/Skyrim Special Edition'
15
+ end
16
+
17
+ it 'returns the downloads directory from the ini file' do
18
+ setup_mo(ini: { Settings: { download_directory: '/path/to/downloads' } })
19
+ expect(mod_organizer.downloads_dir).to eq '/path/to/downloads'
20
+ end
21
+
22
+ it 'returns mod names from the mods directory specified in the ini file' do
23
+ mods_dir = "#{Dir.tmpdir}/ModOrganizerTest/AlternativeMods"
24
+ setup_mo(ini: { Settings: { mod_directory: mods_dir } })
25
+ FileUtils.mkdir_p("#{mods_dir}/TestMod1")
26
+ FileUtils.mkdir_p("#{mods_dir}/TestMod2")
27
+ expect(mod_organizer.mod_names.sort).to eq %w[TestMod1 TestMod2]
28
+ end
29
+
30
+ it 'retrieves the modlist from the profiles directory and the profile specified in the ini file' do
31
+ setup_mo(
32
+ ini: {
33
+ Settings: { profiles_directory: "#{Dir.tmpdir}/ModOrganizerTest/AlternativeProfiles" },
34
+ General: { selected_profile: 'TestProfile' }
35
+ }
36
+ )
37
+ FileUtils.mkdir_p("#{Dir.tmpdir}/ModOrganizerTest/AlternativeProfiles/TestProfile")
38
+ File.write(
39
+ "#{Dir.tmpdir}/ModOrganizerTest/AlternativeProfiles/TestProfile/modlist.txt",
40
+ <<~EO_MOD_LIST
41
+ -TestMod2
42
+ +TestMod1
43
+ EO_MOD_LIST
44
+ )
45
+ expect(mod_organizer.mods_list).to eq %w[TestMod1 TestMod2]
46
+ end
47
+
48
+ it 'retrieves a download by its file name from the downloads directory specified in the ini file' do
49
+ downloads_dir = "#{Dir.tmpdir}/ModOrganizerTest/AlternativeDownloads"
50
+ setup_mo(ini: { Settings: { download_directory: downloads_dir } })
51
+ FileUtils.mkdir_p(downloads_dir)
52
+ downloaded_file = "#{downloads_dir}/TestMod.7z"
53
+ File.write(downloaded_file, 'TestMod downloaded content')
54
+ expect(mod_organizer.download(file_name: 'TestMod.7z').downloaded_file_path).to eq downloaded_file
55
+ end
56
+
57
+ end
58
+
59
+ context 'with the default ini file' do
60
+
61
+ before do
62
+ setup_mo
63
+ end
64
+
65
+ it 'returns the default downloads directory' do
66
+ expect(mod_organizer.downloads_dir).to eq "#{instance_dir}/downloads"
67
+ end
68
+
69
+ it 'runs ModOrganizer' do
70
+ mo_run_from = nil
71
+ expect(mod_organizer).to receive(:system).with('ModOrganizer.exe') do
72
+ mo_run_from = Dir.pwd
73
+ end
74
+ mod_organizer.run
75
+ expect(mo_run_from).to eq instance_dir
76
+ end
77
+
78
+ it 'returns mod names from the default mods directory' do
79
+ FileUtils.mkdir_p("#{instance_dir}/mods/TestMod1")
80
+ FileUtils.mkdir_p("#{instance_dir}/mods/TestMod2")
81
+ expect(mod_organizer.mod_names.sort).to eq %w[TestMod1 TestMod2]
82
+ end
83
+
84
+ it 'returns mod names without considering files' do
85
+ FileUtils.mkdir_p("#{instance_dir}/mods/TestMod1")
86
+ FileUtils.mkdir_p("#{instance_dir}/mods/TestMod2")
87
+ File.write("#{instance_dir}/mods/WrongMod", 'File content')
88
+ expect(mod_organizer.mod_names.sort).to eq %w[TestMod1 TestMod2]
89
+ end
90
+
91
+ it 'caches mod names returned' do
92
+ FileUtils.mkdir_p("#{instance_dir}/mods/TestMod1")
93
+ FileUtils.mkdir_p("#{instance_dir}/mods/TestMod2")
94
+ expect(mod_organizer.mod_names.sort).to eq %w[TestMod1 TestMod2]
95
+ FileUtils.mkdir_p("#{instance_dir}/mods/TestMod3")
96
+ FileUtils.mkdir_p("#{instance_dir}/mods/TestMod4")
97
+ expect(mod_organizer.mod_names.sort).to eq %w[TestMod1 TestMod2]
98
+ end
99
+
100
+ it 'retrieves a mod by its name' do
101
+ FileUtils.mkdir_p("#{instance_dir}/mods/TestMod")
102
+ expect(mod_organizer.mod(name: 'TestMod').name).to eq 'TestMod'
103
+ end
104
+
105
+ it 'does not retrieve an unknown mod' do
106
+ expect(mod_organizer.mod(name: 'UnknownMod')).to be_nil
107
+ end
108
+
109
+ it 'caches mods being retrieved' do
110
+ FileUtils.mkdir_p("#{instance_dir}/mods/TestMod")
111
+ expect(mod_organizer.mod(name: 'TestMod').name).to eq 'TestMod'
112
+ FileUtils.rm_rf("#{instance_dir}/mods/TestMod")
113
+ expect(mod_organizer.mod(name: 'TestMod').name).to eq 'TestMod'
114
+ end
115
+
116
+ it 'retrieves the modlist of the default profile' do
117
+ FileUtils.mkdir_p("#{instance_dir}/profiles/Default")
118
+ File.write(
119
+ "#{instance_dir}/profiles/Default/modlist.txt",
120
+ <<~EO_MOD_LIST
121
+ # This file was automatically generated by Mod Organizer.
122
+ -TestMod2
123
+ +TestMod1
124
+ *Creation Club: ccBGSSSE001-Fish
125
+ *Creation Club: ccBGSSSE025-AdvDSGS
126
+ *Creation Club: ccBGSSSE037-Curios
127
+ *Creation Club: ccQDRSSE001-SurvivalMode
128
+ *DLC: Dawnguard
129
+ *DLC: Dragonborn
130
+ *DLC: HearthFires
131
+ EO_MOD_LIST
132
+ )
133
+ expect(mod_organizer.mods_list).to eq %w[TestMod1 TestMod2]
134
+ end
135
+
136
+ it 'caches the modlist being retrieved' do
137
+ FileUtils.mkdir_p("#{instance_dir}/profiles/Default")
138
+ File.write(
139
+ "#{instance_dir}/profiles/Default/modlist.txt",
140
+ <<~EO_MOD_LIST
141
+ -TestMod2
142
+ +TestMod1
143
+ EO_MOD_LIST
144
+ )
145
+ expect(mod_organizer.mods_list).to eq %w[TestMod1 TestMod2]
146
+ File.write(
147
+ "#{instance_dir}/profiles/Default/modlist.txt",
148
+ <<~EO_MOD_LIST
149
+ -TestMod4
150
+ +TestMod3
151
+ EO_MOD_LIST
152
+ )
153
+ expect(mod_organizer.mods_list).to eq %w[TestMod1 TestMod2]
154
+ end
155
+
156
+ it 'returns the list of enabled mods' do
157
+ FileUtils.mkdir_p("#{instance_dir}/profiles/Default")
158
+ File.write(
159
+ "#{instance_dir}/profiles/Default/modlist.txt",
160
+ <<~EO_MOD_LIST
161
+ -TestMod4
162
+ +TestMod3
163
+ -TestMod2
164
+ +TestMod1
165
+ EO_MOD_LIST
166
+ )
167
+ expect(mod_organizer.enabled_mods).to eq %w[TestMod1 TestMod3]
168
+ end
169
+
170
+ it 'caches the list of enabled mods' do
171
+ FileUtils.mkdir_p("#{instance_dir}/profiles/Default")
172
+ File.write(
173
+ "#{instance_dir}/profiles/Default/modlist.txt",
174
+ <<~EO_MOD_LIST
175
+ -TestMod2
176
+ +TestMod1
177
+ EO_MOD_LIST
178
+ )
179
+ expect(mod_organizer.enabled_mods).to eq %w[TestMod1]
180
+ File.write(
181
+ "#{instance_dir}/profiles/Default/modlist.txt",
182
+ <<~EO_MOD_LIST
183
+ -TestMod4
184
+ +TestMod3
185
+ EO_MOD_LIST
186
+ )
187
+ expect(mod_organizer.enabled_mods).to eq %w[TestMod1]
188
+ end
189
+
190
+ it 'returns the default list of categories' do
191
+ expect(mod_organizer.categories.sort.to_a[0..4].to_h).to eq(
192
+ {
193
+ 1 => 'Animations',
194
+ 2 => 'Armour',
195
+ 3 => 'Audio',
196
+ 4 => 'Cities',
197
+ 5 => 'Clothing'
198
+ }
199
+ )
200
+ end
201
+
202
+ it 'returns the list of categories specific to the ModOrganizer instance' do
203
+ File.write(
204
+ "#{instance_dir}/categories.dat",
205
+ <<~EO_CATEGORIES
206
+ 1|TestAnimations|4,51|0
207
+ 2|TestArmour|5,54|0
208
+ EO_CATEGORIES
209
+ )
210
+ expect(mod_organizer.categories).to eq(
211
+ {
212
+ 1 => 'TestAnimations',
213
+ 2 => 'TestArmour'
214
+ }
215
+ )
216
+ end
217
+
218
+ it 'caches categories returned' do
219
+ File.write(
220
+ "#{instance_dir}/categories.dat",
221
+ <<~EO_CATEGORIES
222
+ 1|TestAnimations|4,51|0
223
+ 2|TestArmour|5,54|0
224
+ EO_CATEGORIES
225
+ )
226
+ expect(mod_organizer.categories).to eq(
227
+ {
228
+ 1 => 'TestAnimations',
229
+ 2 => 'TestArmour'
230
+ }
231
+ )
232
+ File.write(
233
+ "#{instance_dir}/categories.dat",
234
+ <<~EO_CATEGORIES
235
+ 1|TestOtherAnimations|4,51|0
236
+ 2|TestOtherArmour|5,54|0
237
+ 3|TestOtherCities|5,54|0
238
+ EO_CATEGORIES
239
+ )
240
+ expect(mod_organizer.categories).to eq(
241
+ {
242
+ 1 => 'TestAnimations',
243
+ 2 => 'TestArmour'
244
+ }
245
+ )
246
+ end
247
+
248
+ it 'retrieves a download by its file name' do
249
+ FileUtils.mkdir_p("#{instance_dir}/downloads")
250
+ downloaded_file = "#{instance_dir}/downloads/TestMod.7z"
251
+ File.write(downloaded_file, 'TestMod downloaded content')
252
+ expect(mod_organizer.download(file_name: 'TestMod.7z').downloaded_file_path).to eq downloaded_file
253
+ end
254
+
255
+ it 'does not retrieve a download for an unknown file' do
256
+ expect(mod_organizer.download(file_name: 'UnknownMod.7z')).to be_nil
257
+ end
258
+
259
+ it 'caches a download returned' do
260
+ FileUtils.mkdir_p("#{instance_dir}/downloads")
261
+ downloaded_file = "#{instance_dir}/downloads/TestMod.7z"
262
+ File.write(downloaded_file, 'TestMod downloaded content')
263
+ expect(mod_organizer.download(file_name: 'TestMod.7z').downloaded_file_path).to eq downloaded_file
264
+ File.unlink(downloaded_file)
265
+ expect(mod_organizer.download(file_name: 'TestMod.7z')).not_to be_nil
266
+ end
267
+
268
+ end
269
+
270
+ end
271
+
272
+ context 'with a portable installation' do
273
+
274
+ it_behaves_like 'a ModOrganizer instance' do
275
+ before do
276
+ @instance_name = nil
277
+ end
278
+ end
279
+
280
+ end
281
+
282
+ context 'with a shared installation' do
283
+
284
+ it_behaves_like 'a ModOrganizer instance' do
285
+ before do
286
+ @instance_name = 'TestInstance'
287
+ end
288
+ end
289
+
290
+ end
291
+
292
+ end
@@ -0,0 +1,31 @@
1
+ require 'json'
2
+
3
+ describe 'Coding guidelines' do
4
+
5
+ it 'makes sure code style follow Rubocop guides' do
6
+ rubocop_report = JSON.parse(`bundle exec rubocop --format json`)
7
+ expect(rubocop_report['summary']['offense_count']).to(
8
+ eq(0),
9
+ proc do
10
+ # Format a great error message to help
11
+ wrong_files = rubocop_report['files'].reject { |file_info| file_info['offenses'].empty? }
12
+ <<~EO_ERROR
13
+ #{wrong_files.size} files have Rubocop issues:
14
+ #{
15
+ wrong_files.map do |file_info|
16
+ offenses = file_info['offenses'].map { |offense_info| "L#{offense_info['location']['start_line']}: #{offense_info['cop_name']} - #{offense_info['message']}" }
17
+ "* #{file_info['path']}:#{
18
+ if offenses.size == 1
19
+ " #{offenses.first}"
20
+ else
21
+ " #{offenses.size} offenses:\n#{offenses.map { |offense| " - #{offense}" }.join("\n")}"
22
+ end
23
+ }"
24
+ end.join("\n")
25
+ }
26
+ EO_ERROR
27
+ end
28
+ )
29
+ end
30
+
31
+ end
@@ -0,0 +1,100 @@
1
+ require 'mod_organizer_test/helpers'
2
+
3
+ # This file was generated by the `rspec --init` command. Conventionally, all
4
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
5
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
6
+ # this file to always be loaded, without a need to explicitly require it in any
7
+ # files.
8
+ #
9
+ # Given that it is always loaded, you are encouraged to keep this file as
10
+ # light-weight as possible. Requiring heavyweight dependencies from this file
11
+ # will add to the boot time of your test suite on EVERY test run, even for an
12
+ # individual file that may not need all of that loaded. Instead, consider making
13
+ # a separate helper file that requires the additional dependencies and performs
14
+ # the additional setup, and require it from the spec files that actually need
15
+ # it.
16
+ #
17
+ # See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
18
+ RSpec.configure do |config|
19
+ # rspec-expectations config goes here. You can use an alternate
20
+ # assertion/expectation library such as wrong or the stdlib/minitest
21
+ # assertions if you prefer.
22
+ config.expect_with :rspec do |expectations|
23
+ # This option will default to `true` in RSpec 4. It makes the `description`
24
+ # and `failure_message` of custom matchers include text for helper methods
25
+ # defined using `chain`, e.g.:
26
+ # be_bigger_than(2).and_smaller_than(4).description
27
+ # # => "be bigger than 2 and smaller than 4"
28
+ # ...rather than:
29
+ # # => "be bigger than 2"
30
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
31
+ end
32
+
33
+ # rspec-mocks config goes here. You can use an alternate test double
34
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
35
+ config.mock_with :rspec do |mocks|
36
+ # Prevents you from mocking or stubbing a method that does not exist on
37
+ # a real object. This is generally recommended, and will default to
38
+ # `true` in RSpec 4.
39
+ mocks.verify_partial_doubles = true
40
+ end
41
+
42
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
43
+ # have no way to turn it off -- the option exists only for backwards
44
+ # compatibility in RSpec 3). It causes shared context metadata to be
45
+ # inherited by the metadata hash of host groups and examples, rather than
46
+ # triggering implicit auto-inclusion in groups with matching metadata.
47
+ config.shared_context_metadata_behavior = :apply_to_host_groups
48
+
49
+ # The settings below are suggested to provide a good initial experience
50
+ # with RSpec, but feel free to customize to your heart's content.
51
+
52
+ # This allows you to limit a spec run to individual examples or groups
53
+ # you care about by tagging them with `:focus` metadata. When nothing
54
+ # is tagged with `:focus`, all examples get run. RSpec also provides
55
+ # aliases for `it`, `describe`, and `context` that include `:focus`
56
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
57
+ # config.filter_run_when_matching :focus
58
+
59
+ # Allows RSpec to persist some state between runs in order to support
60
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
61
+ # you configure your source control system to ignore this file.
62
+ # config.example_status_persistence_file_path = "spec/examples.txt"
63
+
64
+ # Limits the available syntax to the non-monkey patched syntax that is
65
+ # recommended. For more details, see:
66
+ # https://relishapp.com/rspec/rspec-core/docs/configuration/zero-monkey-patching-mode
67
+ # config.disable_monkey_patching!
68
+
69
+ # This setting enables warnings. It's recommended, but in some cases may
70
+ # be too noisy due to issues in dependencies.
71
+ # config.warnings = true
72
+
73
+ # Many RSpec users commonly either run the entire suite or an individual
74
+ # file, and it's useful to allow more verbose output when running an
75
+ # individual spec file.
76
+ # if config.files_to_run.one?
77
+ # # Use the documentation formatter for detailed output,
78
+ # # unless a formatter has already been configured
79
+ # # (e.g. via a command-line flag).
80
+ # config.default_formatter = "doc"
81
+ # end
82
+
83
+ # Print the 10 slowest examples and example groups at the
84
+ # end of the spec run, to help surface which specs are running
85
+ # particularly slow.
86
+ # config.profile_examples = 10
87
+
88
+ # Run specs in random order to surface order dependencies. If you find an
89
+ # order dependency and want to debug it, you can fix the order by providing
90
+ # the seed, which is printed after each run.
91
+ # --seed 1234
92
+ # config.order = :random
93
+
94
+ # Seed global randomization in this process using the `--seed` CLI option.
95
+ # Setting this allows you to use `--seed` to deterministically reproduce
96
+ # test failures related to randomization by passing the same `--seed` value
97
+ # as the one that triggered the failure.
98
+ # Kernel.srand config.seed
99
+
100
+ end
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mod_organizer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Muriel Salvan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-01-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: inifile
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: memoist3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sem_ver_components
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.42'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.42'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.16'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.16'
97
+ description:
98
+ email:
99
+ - muriel@x-aeon.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files:
103
+ - CHANGELOG.md
104
+ - LICENSE.md
105
+ - README.md
106
+ files:
107
+ - CHANGELOG.md
108
+ - LICENSE.md
109
+ - README.md
110
+ - lib/default_categories.dat
111
+ - lib/mod_organizer.rb
112
+ - lib/mod_organizer/download.rb
113
+ - lib/mod_organizer/mod.rb
114
+ - lib/mod_organizer/source.rb
115
+ - lib/mod_organizer/utils.rb
116
+ - lib/mod_organizer/version.rb
117
+ - spec/mod_organizer_test/helpers.rb
118
+ - spec/mod_organizer_test/scenarios/mod_organizer/download_spec.rb
119
+ - spec/mod_organizer_test/scenarios/mod_organizer/mod_spec.rb
120
+ - spec/mod_organizer_test/scenarios/mod_organizer/source_spec.rb
121
+ - spec/mod_organizer_test/scenarios/mod_organizer_spec.rb
122
+ - spec/mod_organizer_test/scenarios/rubocop_spec.rb
123
+ - spec/spec_helper.rb
124
+ homepage: https://github.com/Muriel-Salvan/mod_organizer
125
+ licenses:
126
+ - BSD-3-Clause
127
+ metadata:
128
+ homepage_uri: https://github.com/Muriel-Salvan/mod_organizer
129
+ rubygems_mfa_required: 'true'
130
+ post_install_message:
131
+ rdoc_options: []
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '3.1'
139
+ required_rubygems_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ requirements: []
145
+ rubygems_version: 3.3.26
146
+ signing_key:
147
+ specification_version: 4
148
+ summary: Ruby API accessing a Mod Organizer instance
149
+ test_files: []