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,115 @@
1
+ require 'fileutils'
2
+ require 'logger'
3
+ require 'mod_organizer'
4
+
5
+ module ModOrganizerTest
6
+
7
+ module Helpers
8
+
9
+ # ModOrganizer: The ModOrganizer instance that has been setup
10
+ attr_reader :mod_organizer
11
+
12
+ # String: The instance directory that has been setup
13
+ attr_reader :instance_dir
14
+
15
+ # Prepare a ModOrganizer instance directory
16
+ # The directory path is stored in an instance variable named @instance_dir.
17
+ #
18
+ # Parameters::
19
+ # * *ini* (Hash< Symbol, Hash<Symbol, String> >): Content of the ini file to overwrite default values [default: {}]
20
+ # * *instance_name* (String or nil): The instance name to be used to mock a shared installation, or nil for a portable installation [default: nil]
21
+ def setup_instance_dir(ini: {}, instance_name: nil)
22
+ @instance_dir = "#{Dir.tmpdir}/ModOrganizerTest/ModOrganizer/#{instance_name || 'PortableInstance'}"
23
+ FileUtils.rm_rf(@instance_dir)
24
+ FileUtils.mkdir_p(@instance_dir)
25
+ IniFile.new(
26
+ content: {
27
+ General: {
28
+ gamePath: 'C:/path/to/game',
29
+ selected_profile: 'Default'
30
+ },
31
+ Settings: {
32
+ log_level: '1'
33
+ }
34
+ }.merge(ini) do |_section, default_properties, overwrite_properties|
35
+ default_properties.merge(overwrite_properties)
36
+ end
37
+ ).write(filename: "#{@instance_dir}/ModOrganizer.ini")
38
+ end
39
+
40
+ # Setup an instance of ModOrganizer setup on an instance directory.
41
+ # It uses the variable @instance_name to eventually setup a shared installation.
42
+ # The instance is stored in an instance variable named @mod_organizer.
43
+ #
44
+ # Parameters::
45
+ # * *ini* (Hash< Symbol, Hash<Symbol, String> >): Content of the ini file to overwrite default values [default: {}]
46
+ def setup_mo(ini: {})
47
+ setup_instance_dir(ini:, instance_name: @instance_name)
48
+ mo_logger = StringIO.new
49
+ ENV['LOCALAPPDATA'] = "#{Dir.tmpdir}/ModOrganizerTest"
50
+ @mod_organizer = ModOrganizer.new(@instance_dir, instance_name: @instance_name, logger: Logger.new(mo_logger))
51
+ end
52
+
53
+ # Setup a mod in the default mods folder.
54
+ # Prerequisite: instance_dir has to be setup previously with setup_instance_dir.
55
+ #
56
+ # Parameters::
57
+ # * *ini* (Hash< Symbol, Hash<Symbol, String> >): Content of the meta ini file to overwrite default values [default: {}]
58
+ # * *mod_name* (String): The mod name [default: 'TestMod']
59
+ # Result::
60
+ # * String: The mod directory
61
+ def setup_mod(ini: {}, mod_name: 'TestMod')
62
+ mod_dir = "#{instance_dir}/mods/#{mod_name}"
63
+ FileUtils.mkdir_p(mod_dir)
64
+ IniFile.new(
65
+ content: {
66
+ General: {
67
+ category: '2,',
68
+ url: 'https://test-mod.url',
69
+ installationFile: 'TestMod-v1.7z'
70
+ },
71
+ installedFiles: {
72
+ size: '1',
73
+ '1\\modid': '1337',
74
+ '1\\fileid': '666'
75
+ }
76
+ }.merge(ini) do |_section, default_properties, overwrite_properties|
77
+ default_properties.merge(overwrite_properties)
78
+ end
79
+ ).write(filename: "#{mod_dir}/meta.ini")
80
+ mod_dir
81
+ end
82
+
83
+ # Setup a download in the default downloads folder.
84
+ # Prerequisite: instance_dir has to be setup previously with setup_instance_dir.
85
+ #
86
+ # Parameters::
87
+ # * *ini* (Hash< Symbol, Hash<Symbol, String> >): Content of the meta ini file to overwrite default values [default: {}]
88
+ # * *file_name* (String): The downloaded file name [default: 'TestMod-v1.7z']
89
+ # Result::
90
+ # * String: The downloads directory
91
+ def setup_download(ini: {}, file_name: 'TestMod-v1.7z')
92
+ downloads_dir = "#{instance_dir}/downloads"
93
+ FileUtils.mkdir_p(downloads_dir)
94
+ IniFile.new(
95
+ content: {
96
+ General: {
97
+ name: 'Test Mod file',
98
+ modID: '1107',
99
+ fileID: '42'
100
+ }
101
+ }.merge(ini) do |_section, default_properties, overwrite_properties|
102
+ default_properties.merge(overwrite_properties)
103
+ end
104
+ ).write(filename: "#{downloads_dir}/#{file_name}.meta")
105
+ File.write("#{downloads_dir}/#{file_name}", "#{file_name} downloaded content")
106
+ downloads_dir
107
+ end
108
+
109
+ end
110
+
111
+ end
112
+
113
+ RSpec.configure do |config|
114
+ config.include ModOrganizerTest::Helpers
115
+ end
@@ -0,0 +1,55 @@
1
+ require 'time'
2
+
3
+ describe ModOrganizer::Download do
4
+
5
+ let(:download) { mod_organizer.mod(name: 'TestMod').sources.first.download }
6
+ let(:download_dir) { "#{instance_dir}/downloads" }
7
+ let(:downloaded_file) { "#{instance_dir}/downloads/TestMod-v1.7z" }
8
+
9
+ before do
10
+ setup_mo
11
+ setup_mod
12
+ setup_download
13
+ end
14
+
15
+ it 'returns the downloaded file path' do
16
+ expect(download.downloaded_file_path).to eq downloaded_file
17
+ end
18
+
19
+ it 'returns the downloaded file date' do
20
+ file_date = Time.parse('2023-01-05 10:20:30 UTC')
21
+ File.utime(File.atime(downloaded_file), file_date, downloaded_file)
22
+ expect(download.downloaded_date).to eq file_date
23
+ end
24
+
25
+ it 'returns the NexusMods file name' do
26
+ expect(download.nexus_file_name).to eq 'Test Mod file'
27
+ end
28
+
29
+ it 'caches the returned NexusMods file name' do
30
+ expect(download.nexus_file_name).to eq 'Test Mod file'
31
+ setup_download(ini: { General: { name: 'Alternative Test Mod file' } })
32
+ expect(download.nexus_file_name).to eq 'Test Mod file'
33
+ end
34
+
35
+ it 'returns the NexusMods mod id' do
36
+ expect(download.nexus_mod_id).to eq 1107
37
+ end
38
+
39
+ it 'caches the returned NexusMods mod id' do
40
+ expect(download.nexus_mod_id).to eq 1107
41
+ setup_download(ini: { General: { modID: '666' } })
42
+ expect(download.nexus_mod_id).to eq 1107
43
+ end
44
+
45
+ it 'returns the NexusMods file id' do
46
+ expect(download.nexus_file_id).to eq 42
47
+ end
48
+
49
+ it 'caches the returned NexusMods file id' do
50
+ expect(download.nexus_file_id).to eq 42
51
+ setup_download(ini: { General: { fileID: '666' } })
52
+ expect(download.nexus_file_id).to eq 42
53
+ end
54
+
55
+ end
@@ -0,0 +1,184 @@
1
+ describe ModOrganizer::Mod do
2
+
3
+ let(:mod) { mod_organizer.mod(name: 'TestMod') }
4
+ let(:mod_dir) { "#{instance_dir}/mods/TestMod" }
5
+
6
+ context 'with a simple mod' do
7
+
8
+ before do
9
+ setup_mo
10
+ setup_mod
11
+ end
12
+
13
+ it 'returns the mod name' do
14
+ expect(mod.name).to eq 'TestMod'
15
+ end
16
+
17
+ it 'returns the mod enabled flag when enabled' do
18
+ FileUtils.mkdir_p("#{instance_dir}/profiles/Default")
19
+ File.write(
20
+ "#{instance_dir}/profiles/Default/modlist.txt",
21
+ <<~EO_MOD_LIST
22
+ +TestMod
23
+ EO_MOD_LIST
24
+ )
25
+ expect(mod.enabled?).to be(true)
26
+ end
27
+
28
+ it 'returns the mod enabled flag when disabled' do
29
+ FileUtils.mkdir_p("#{instance_dir}/profiles/Default")
30
+ File.write(
31
+ "#{instance_dir}/profiles/Default/modlist.txt",
32
+ <<~EO_MOD_LIST
33
+ -TestMod
34
+ EO_MOD_LIST
35
+ )
36
+ expect(mod.enabled?).to be(false)
37
+ end
38
+
39
+ it 'returns the mod categories' do
40
+ expect(mod.categories).to eq %w[Armour]
41
+ end
42
+
43
+ it 'returns no plugins for mods having no plugins' do
44
+ expect(mod.plugins).to eq []
45
+ end
46
+
47
+ it 'returns plugins from the mod\'s root directory' do
48
+ File.write("#{mod_dir}/plugin1.esm", 'Plugin1 esm content')
49
+ File.write("#{mod_dir}/plugin2.esp", 'Plugin2 esp content')
50
+ File.write("#{mod_dir}/plugin3.esl", 'Plugin3 esl content')
51
+ File.write("#{mod_dir}/other_file.txt", 'Other file content')
52
+ FileUtils.mkdir_p("#{mod_dir}/others")
53
+ File.write("#{mod_dir}/others/plugin4.esp", 'Plugin4 esp content')
54
+ expect(mod.plugins.sort).to eq %w[
55
+ plugin1.esm
56
+ plugin2.esp
57
+ plugin3.esl
58
+ ]
59
+ end
60
+
61
+ it 'returns plugins in lower case' do
62
+ File.write("#{mod_dir}/PlUgIn1.esm", 'Plugin1 esm content')
63
+ expect(mod.plugins).to eq %w[plugin1.esm]
64
+ end
65
+
66
+ it 'caches plugins being returned' do
67
+ File.write("#{mod_dir}/plugin1.esm", 'Plugin1 esm content')
68
+ expect(mod.plugins).to eq %w[plugin1.esm]
69
+ File.unlink("#{mod_dir}/plugin1.esm")
70
+ expect(mod.plugins).to eq %w[plugin1.esm]
71
+ end
72
+
73
+ it 'returns the mod source' do
74
+ sources = mod.sources
75
+ expect(sources.size).to be 1
76
+ expect(sources.first.type).to be :nexus_mods
77
+ expect(sources.first.nexus_mod_id).to be 1337
78
+ expect(sources.first.nexus_file_id).to be 666
79
+ expect(sources.first.file_name).to eq 'TestMod-v1.7z'
80
+ end
81
+
82
+ it 'caches the returned the mod source' do
83
+ expect(mod.sources.first.nexus_mod_id).to be 1337
84
+ setup_mod(ini: { installedFiles: { '1\\modid': '1107' } })
85
+ expect(mod.sources.first.nexus_mod_id).to be 1337
86
+ end
87
+
88
+ it 'returns the mod URL' do
89
+ expect(mod.url).to eq 'https://test-mod.url'
90
+ end
91
+
92
+ end
93
+
94
+ context 'with a mod having no ini file' do
95
+
96
+ before do
97
+ setup_mo
98
+ setup_mod
99
+ File.unlink("#{mod_dir}/meta.ini")
100
+ end
101
+
102
+ it 'returns the mod name' do
103
+ expect(mod.name).to eq 'TestMod'
104
+ end
105
+
106
+ it 'returns the mod enabled flag when enabled' do
107
+ FileUtils.mkdir_p("#{instance_dir}/profiles/Default")
108
+ File.write(
109
+ "#{instance_dir}/profiles/Default/modlist.txt",
110
+ <<~EO_MOD_LIST
111
+ +TestMod
112
+ EO_MOD_LIST
113
+ )
114
+ expect(mod.enabled?).to be(true)
115
+ end
116
+
117
+ it 'returns the mod enabled flag when disabled' do
118
+ FileUtils.mkdir_p("#{instance_dir}/profiles/Default")
119
+ File.write(
120
+ "#{instance_dir}/profiles/Default/modlist.txt",
121
+ <<~EO_MOD_LIST
122
+ -TestMod
123
+ EO_MOD_LIST
124
+ )
125
+ expect(mod.enabled?).to be(false)
126
+ end
127
+
128
+ it 'returns no mod categories' do
129
+ expect(mod.categories).to eq []
130
+ end
131
+
132
+ it 'returns an unknown mod source' do
133
+ sources = mod.sources
134
+ expect(sources.size).to be 1
135
+ expect(sources.first.type).to be :unknown
136
+ expect(sources.first.nexus_mod_id).to be_nil
137
+ expect(sources.first.nexus_file_id).to be_nil
138
+ expect(sources.first.file_name).to be_nil
139
+ end
140
+
141
+ it 'returns no mod URL' do
142
+ expect(mod.url).to be_nil
143
+ end
144
+
145
+ end
146
+
147
+ context 'with a mod having several sources' do
148
+
149
+ before do
150
+ setup_mo
151
+ setup_mod(
152
+ ini: {
153
+ installedFiles: {
154
+ size: '3',
155
+ '1\\modid': '100',
156
+ '1\\fileid': '200',
157
+ '2\\modid': '101',
158
+ '2\\fileid': '201',
159
+ '3\\modid': '102',
160
+ '3\\fileid': '202'
161
+ }
162
+ }
163
+ )
164
+ end
165
+
166
+ it 'returns several sources' do
167
+ expect(mod.sources.map.with_index { |source, idx| [idx, source.nexus_mod_id, source.nexus_file_id] }.sort).to eq [
168
+ [0, 100, 200],
169
+ [1, 101, 201],
170
+ [2, 102, 202]
171
+ ]
172
+ end
173
+
174
+ it 'associates the downloaded file name to the last source only' do
175
+ expect(mod.sources.map.with_index { |source, idx| [idx, source.file_name] }.sort).to eq [
176
+ [0, nil],
177
+ [1, nil],
178
+ [2, 'TestMod-v1.7z']
179
+ ]
180
+ end
181
+
182
+ end
183
+
184
+ end
@@ -0,0 +1,71 @@
1
+ describe ModOrganizer::Source do
2
+
3
+ let(:source) { mod_organizer.mod(name: 'TestMod').sources.first }
4
+
5
+ context 'with a source from nexus_mods' do
6
+
7
+ before do
8
+ setup_mo
9
+ setup_mod
10
+ end
11
+
12
+ it 'returns the NexusMods mod id' do
13
+ expect(source.nexus_mod_id).to be 1337
14
+ end
15
+
16
+ it 'returns the NexusMods file id' do
17
+ expect(source.nexus_file_id).to be 666
18
+ end
19
+
20
+ it 'returns the source file name' do
21
+ expect(source.file_name).to eq 'TestMod-v1.7z'
22
+ end
23
+
24
+ it 'returns the source type' do
25
+ expect(source.type).to be :nexus_mods
26
+ end
27
+
28
+ it 'returns the download info of the source' do
29
+ FileUtils.mkdir_p("#{instance_dir}/downloads")
30
+ downloaded_file = "#{instance_dir}/downloads/TestMod-v1.7z"
31
+ File.write(downloaded_file, 'TestMod v1 downloaded content')
32
+ expect(source.download.downloaded_file_path).to eq downloaded_file
33
+ end
34
+
35
+ it 'returns no download info of the source if the downloaded file is missing' do
36
+ expect(source.download).to be_nil
37
+ end
38
+
39
+ end
40
+
41
+ context 'with an unknown source' do
42
+
43
+ before do
44
+ setup_mo
45
+ setup_mod
46
+ File.unlink("#{instance_dir}/mods/TestMod/meta.ini")
47
+ end
48
+
49
+ it 'returns no NexusMods mod id' do
50
+ expect(source.nexus_mod_id).to be_nil
51
+ end
52
+
53
+ it 'returns no NexusMods file id' do
54
+ expect(source.nexus_file_id).to be_nil
55
+ end
56
+
57
+ it 'returns no source file name' do
58
+ expect(source.file_name).to be_nil
59
+ end
60
+
61
+ it 'returns the source type' do
62
+ expect(source.type).to be :unknown
63
+ end
64
+
65
+ it 'returns no download info of the source' do
66
+ expect(source.download).to be_nil
67
+ end
68
+
69
+ end
70
+
71
+ end