• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Ortham / libloadorder / 6292826118

24 Sep 2023 10:42PM UTC coverage: 88.326% (+0.2%) from 88.158%
6292826118

push

github

Ortham
Add lo_get_early_loading_plugins()

Now that the list of implicitly active plugins can include plugins
without hardcoded positions (which was already true for Skyrim's
Update.esm, but it is treated as hardcoded),
lo_get_early_loading_plugins() exposes the old list of implicitly active
plugins, i.e. those with hardcoded positions.

35 of 35 new or added lines in 1 file covered. (100.0%)

5879 of 6656 relevant lines covered (88.33%)

77965.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.57
/src/load_order/asterisk_based.rs
1
/*
2
 * This file is part of libloadorder
3
 *
4
 * Copyright (C) 2017 Oliver Hamlet
5
 *
6
 * libloadorder is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * libloadorder is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with libloadorder. If not, see <http://www.gnu.org/licenses/>.
18
 */
19
use std::collections::HashSet;
20
use std::fs::File;
21
use std::io::{BufWriter, Write};
22

23
use unicase::eq;
24

25
use super::mutable::{generic_insert_position, hoist_masters, read_plugin_names, MutableLoadOrder};
26
use super::readable::{ReadableLoadOrder, ReadableLoadOrderBase};
27
use super::strict_encode;
28
use super::writable::{
29
    activate, add, create_parent_dirs, deactivate, remove, set_active_plugins, WritableLoadOrder,
30
};
31
use crate::enums::{Error, GameId};
32
use crate::game_settings::GameSettings;
33
use crate::plugin::{trim_dot_ghost, Plugin};
34

35
#[derive(Clone, Debug)]
×
36
pub struct AsteriskBasedLoadOrder {
37
    game_settings: GameSettings,
38
    plugins: Vec<Plugin>,
39
}
40

41
impl AsteriskBasedLoadOrder {
42
    pub fn new(game_settings: GameSettings) -> Self {
×
43
        Self {
×
44
            game_settings,
×
45
            plugins: Vec::new(),
×
46
        }
×
47
    }
×
48

49
    fn read_from_active_plugins_file(&self) -> Result<Vec<(String, bool)>, Error> {
20✔
50
        if self.ignore_active_plugins_file() {
20✔
51
            Ok(Vec::new())
×
52
        } else {
53
            read_plugin_names(
20✔
54
                self.game_settings().active_plugins_file(),
20✔
55
                owning_plugin_line_mapper,
20✔
56
            )
20✔
57
        }
58
    }
20✔
59

60
    fn ignore_active_plugins_file(&self) -> bool {
24✔
61
        // Fallout 4 ignores plugins.txt if there are any sTestFile plugins listed in the ini files. The implicitly active plugins are the early loading plugins plus test file plugins.
24✔
62
        self.game_settings.id() == GameId::Fallout4
24✔
63
            && self.game_settings.implicitly_active_plugins().len()
1✔
64
                > self.game_settings.early_loading_plugins().len()
1✔
65
    }
24✔
66
}
67

68
impl ReadableLoadOrderBase for AsteriskBasedLoadOrder {
69
    fn game_settings_base(&self) -> &GameSettings {
21,612✔
70
        &self.game_settings
21,612✔
71
    }
21,612✔
72

73
    fn plugins(&self) -> &[Plugin] {
30,054✔
74
        &self.plugins
30,054✔
75
    }
30,054✔
76
}
77

78
impl MutableLoadOrder for AsteriskBasedLoadOrder {
79
    fn plugins_mut(&mut self) -> &mut Vec<Plugin> {
19,479✔
80
        &mut self.plugins
19,479✔
81
    }
19,479✔
82

83
    fn insert_position(&self, plugin: &Plugin) -> Option<usize> {
10,646✔
84
        if self.game_settings().loads_early(plugin.name()) {
10,646✔
85
            if self.plugins().is_empty() {
24✔
86
                return None;
3✔
87
            }
21✔
88

21✔
89
            let mut loaded_plugin_count = 0;
21✔
90
            for plugin_name in self.game_settings().early_loading_plugins() {
31✔
91
                if eq(plugin.name(), plugin_name) {
31✔
92
                    return Some(loaded_plugin_count);
21✔
93
                }
10✔
94

10✔
95
                if self.index_of(plugin_name).is_some() {
10✔
96
                    loaded_plugin_count += 1;
3✔
97
                }
7✔
98
            }
99
        }
10,622✔
100

101
        generic_insert_position(self.plugins(), plugin)
10,622✔
102
    }
10,646✔
103
}
104

105
impl WritableLoadOrder for AsteriskBasedLoadOrder {
106
    fn game_settings_mut(&mut self) -> &mut GameSettings {
×
107
        &mut self.game_settings
×
108
    }
×
109

110
    fn load(&mut self) -> Result<(), Error> {
20✔
111
        self.plugins_mut().clear();
20✔
112

113
        let plugin_tuples = self.read_from_active_plugins_file()?;
20✔
114
        let filenames = self.find_plugins_sorted();
20✔
115

20✔
116
        self.load_unique_plugins(plugin_tuples, filenames);
20✔
117
        hoist_masters(&mut self.plugins)?;
20✔
118

119
        self.add_implicitly_active_plugins()?;
20✔
120

121
        Ok(())
20✔
122
    }
20✔
123

124
    fn save(&mut self) -> Result<(), Error> {
125
        create_parent_dirs(self.game_settings().active_plugins_file())?;
4✔
126

127
        let file = File::create(self.game_settings().active_plugins_file())?;
4✔
128
        let mut writer = BufWriter::new(file);
4✔
129
        for plugin in self.plugins() {
14✔
130
            if self.game_settings().loads_early(plugin.name()) {
14✔
131
                // Skip early loading plugins, but not implicitly active plugins
132
                // as they may need load order positions defined.
133
                continue;
4✔
134
            }
10✔
135

10✔
136
            if plugin.is_active() {
10✔
137
                write!(writer, "*")?;
4✔
138
            }
6✔
139
            writer.write_all(&strict_encode(plugin.name())?)?;
10✔
140
            writeln!(writer)?;
9✔
141
        }
142

143
        Ok(())
3✔
144
    }
4✔
145

146
    fn add(&mut self, plugin_name: &str) -> Result<usize, Error> {
×
147
        add(self, plugin_name)
×
148
    }
×
149

150
    fn remove(&mut self, plugin_name: &str) -> Result<(), Error> {
×
151
        remove(self, plugin_name)
×
152
    }
×
153

154
    fn set_load_order(&mut self, plugin_names: &[&str]) -> Result<(), Error> {
7✔
155
        let is_game_master_first = plugin_names
7✔
156
            .first()
7✔
157
            .map(|name| eq(*name, self.game_settings().master_file()))
7✔
158
            .unwrap_or(false);
7✔
159
        if !is_game_master_first {
7✔
160
            return Err(Error::GameMasterMustLoadFirst);
2✔
161
        }
5✔
162

5✔
163
        // Check that all early loading plugins that are present load in
5✔
164
        // their hardcoded order.
5✔
165
        let mut missing_plugins_count = 0;
5✔
166
        for (i, plugin_name) in self
22✔
167
            .game_settings()
5✔
168
            .early_loading_plugins()
5✔
169
            .iter()
5✔
170
            .enumerate()
5✔
171
        {
172
            match plugin_names.iter().position(|n| eq(*n, plugin_name)) {
99✔
173
                Some(pos) => {
7✔
174
                    if pos != i - missing_plugins_count {
7✔
175
                        return Err(Error::GameMasterMustLoadFirst);
1✔
176
                    }
6✔
177
                }
178
                None => missing_plugins_count += 1,
15✔
179
            }
180
        }
181

182
        self.replace_plugins(plugin_names)
4✔
183
    }
7✔
184

185
    fn set_plugin_index(&mut self, plugin_name: &str, position: usize) -> Result<usize, Error> {
3✔
186
        if position != 0
3✔
187
            && !self.plugins().is_empty()
2✔
188
            && eq(plugin_name, self.game_settings().master_file())
2✔
189
        {
190
            return Err(Error::GameMasterMustLoadFirst);
1✔
191
        }
2✔
192
        if position == 0 && !eq(plugin_name, self.game_settings().master_file()) {
2✔
193
            return Err(Error::GameMasterMustLoadFirst);
1✔
194
        }
1✔
195

1✔
196
        self.move_or_insert_plugin_with_index(plugin_name, position)
1✔
197
    }
3✔
198

199
    fn is_self_consistent(&self) -> Result<bool, Error> {
1✔
200
        Ok(true)
1✔
201
    }
1✔
202

203
    /// An asterisk-based load order can be ambiguous if there are installed
204
    /// plugins that don't exist in the active plugins file.
205
    fn is_ambiguous(&self) -> Result<bool, Error> {
4✔
206
        let mut set: HashSet<String> = HashSet::new();
4✔
207

4✔
208
        // Read plugins from the active plugins file. A set of plugin names is
4✔
209
        // more useful than the returned vec, so insert into the set during the
4✔
210
        // line mapping and then discard the line.
4✔
211
        if !self.ignore_active_plugins_file() {
4✔
212
            read_plugin_names(self.game_settings().active_plugins_file(), |line| {
12✔
213
                plugin_line_mapper(line).and_then::<(), _>(|(name, _)| {
12✔
214
                    set.insert(trim_dot_ghost(name).to_lowercase());
12✔
215
                    None
12✔
216
                })
12✔
217
            })?;
12✔
218
        }
×
219

220
        // Check if all loaded plugins aside from early loading plugins
221
        // (which don't get written to the active plugins file) are named in the
222
        // set.
223
        let all_plugins_listed = self
4✔
224
            .plugins
4✔
225
            .iter()
4✔
226
            .filter(|plugin| !self.game_settings().loads_early(plugin.name()))
13✔
227
            .all(|plugin| set.contains(&plugin.name().to_lowercase()));
8✔
228

4✔
229
        Ok(!all_plugins_listed)
4✔
230
    }
4✔
231

232
    fn activate(&mut self, plugin_name: &str) -> Result<(), Error> {
4✔
233
        activate(self, plugin_name)
4✔
234
    }
4✔
235

236
    fn deactivate(&mut self, plugin_name: &str) -> Result<(), Error> {
×
237
        deactivate(self, plugin_name)
×
238
    }
×
239

240
    fn set_active_plugins(&mut self, active_plugin_names: &[&str]) -> Result<(), Error> {
3✔
241
        set_active_plugins(self, active_plugin_names)
3✔
242
    }
3✔
243
}
244

245
fn plugin_line_mapper(line: &str) -> Option<(&str, bool)> {
10,557✔
246
    if line.is_empty() || line.starts_with('#') {
10,557✔
247
        None
×
248
    } else if line.as_bytes()[0] == b'*' {
10,557✔
249
        Some((&line[1..], true))
10,556✔
250
    } else {
251
        Some((line, false))
1✔
252
    }
253
}
10,557✔
254

255
fn owning_plugin_line_mapper(line: &str) -> Option<(String, bool)> {
10,545✔
256
    plugin_line_mapper(line).map(|(name, active)| (name.to_owned(), active))
10,545✔
257
}
10,545✔
258

259
#[cfg(test)]
260
mod tests {
261
    use super::*;
262

263
    use crate::enums::GameId;
264
    use crate::load_order::tests::*;
265
    use crate::tests::{copy_to_dir, copy_to_test_dir};
266
    use filetime::{set_file_times, FileTime};
267
    use std::fs::{create_dir_all, remove_dir_all, File};
268
    use std::io;
269
    use std::io::{BufRead, BufReader};
270
    use std::path::Path;
271
    use tempfile::tempdir;
272

273
    fn prepare(game_id: GameId, game_dir: &Path) -> AsteriskBasedLoadOrder {
46✔
274
        let (game_settings, plugins) = mock_game_files(game_id, game_dir);
46✔
275
        AsteriskBasedLoadOrder {
46✔
276
            game_settings,
46✔
277
            plugins,
46✔
278
        }
46✔
279
    }
46✔
280

281
    fn prepare_bulk_plugins(game_settings: &GameSettings) -> Vec<String> {
3✔
282
        let mut plugins: Vec<String> = vec![game_settings.master_file().to_string()];
3✔
283
        plugins.extend((0..260).map(|i| format!("Blank{}.esm", i)));
780✔
284
        plugins.extend((0..5000).map(|i| format!("Blank{}.esl", i)));
15,000✔
285

286
        for plugin in &plugins {
15,786✔
287
            copy_to_test_dir("Blank - Different.esm", &plugin, game_settings);
15,783✔
288
        }
15,783✔
289

290
        write_active_plugins_file(game_settings, &plugins);
3✔
291

3✔
292
        plugins
3✔
293
    }
3✔
294

295
    #[test]
1✔
296
    fn insert_position_should_return_none_for_the_game_master_if_no_plugins_are_loaded() {
1✔
297
        let tmp_dir = tempdir().unwrap();
1✔
298
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
299

1✔
300
        load_order.plugins_mut().clear();
1✔
301

1✔
302
        let plugin = Plugin::new("Skyrim.esm", &load_order.game_settings()).unwrap();
1✔
303
        let position = load_order.insert_position(&plugin);
1✔
304

1✔
305
        assert!(position.is_none());
1✔
306
    }
1✔
307

308
    #[test]
1✔
309
    fn insert_position_should_return_the_hardcoded_index_of_an_implicitly_active_plugin() {
1✔
310
        let tmp_dir = tempdir().unwrap();
1✔
311
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
312

1✔
313
        let plugin = Plugin::new("Blank.esm", &load_order.game_settings()).unwrap();
1✔
314
        load_order.plugins_mut().insert(1, plugin);
1✔
315

1✔
316
        copy_to_test_dir("Blank.esm", "HearthFires.esm", &load_order.game_settings());
1✔
317
        let plugin = Plugin::new("HearthFires.esm", &load_order.game_settings()).unwrap();
1✔
318
        let position = load_order.insert_position(&plugin);
1✔
319

1✔
320
        assert_eq!(1, position.unwrap());
1✔
321
    }
1✔
322

323
    #[test]
1✔
324
    fn insert_position_should_not_count_installed_unloaded_implicitly_active_plugins() {
1✔
325
        let tmp_dir = tempdir().unwrap();
1✔
326
        let load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
327

1✔
328
        copy_to_test_dir("Blank.esm", "Update.esm", &load_order.game_settings());
1✔
329
        copy_to_test_dir("Blank.esm", "HearthFires.esm", &load_order.game_settings());
1✔
330
        let plugin = Plugin::new("HearthFires.esm", &load_order.game_settings()).unwrap();
1✔
331
        let position = load_order.insert_position(&plugin);
1✔
332

1✔
333
        assert_eq!(1, position.unwrap());
1✔
334
    }
1✔
335

336
    #[test]
1✔
337
    fn insert_position_should_return_none_if_given_a_non_master_plugin() {
1✔
338
        let tmp_dir = tempdir().unwrap();
1✔
339
        let load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
340

1✔
341
        let plugin =
1✔
342
            Plugin::new("Blank - Master Dependent.esp", &load_order.game_settings()).unwrap();
1✔
343
        let position = load_order.insert_position(&plugin);
1✔
344

1✔
345
        assert_eq!(None, position);
1✔
346
    }
1✔
347

348
    #[test]
1✔
349
    fn insert_position_should_return_the_first_non_master_plugin_index_if_given_a_master_plugin() {
1✔
350
        let tmp_dir = tempdir().unwrap();
1✔
351
        let load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
352

1✔
353
        let plugin = Plugin::new("Blank.esm", &load_order.game_settings()).unwrap();
1✔
354
        let position = load_order.insert_position(&plugin);
1✔
355

1✔
356
        assert_eq!(1, position.unwrap());
1✔
357
    }
1✔
358

359
    #[test]
1✔
360
    fn insert_position_should_return_none_if_no_non_masters_are_present() {
1✔
361
        let tmp_dir = tempdir().unwrap();
1✔
362
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
363

1✔
364
        // Remove non-master plugins from the load order.
1✔
365
        load_order.plugins_mut().retain(|p| p.is_master_file());
3✔
366

1✔
367
        let plugin = Plugin::new("Blank.esm", &load_order.game_settings()).unwrap();
1✔
368
        let position = load_order.insert_position(&plugin);
1✔
369

1✔
370
        assert_eq!(None, position);
1✔
371
    }
1✔
372

373
    #[test]
1✔
374
    fn insert_position_should_return_the_first_non_master_index_if_given_a_light_master() {
1✔
375
        let tmp_dir = tempdir().unwrap();
1✔
376
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
377

1✔
378
        copy_to_test_dir("Blank.esm", "Blank.esl", load_order.game_settings());
1✔
379
        let plugin = Plugin::new("Blank.esl", &load_order.game_settings()).unwrap();
1✔
380

1✔
381
        load_order.plugins_mut().insert(1, plugin);
1✔
382

1✔
383
        let position = load_order.insert_position(&load_order.plugins()[1]);
1✔
384

1✔
385
        assert_eq!(2, position.unwrap());
1✔
386

387
        copy_to_test_dir(
1✔
388
            "Blank.esp",
1✔
389
            "Blank - Different.esl",
1✔
390
            load_order.game_settings(),
1✔
391
        );
1✔
392
        let plugin = Plugin::new("Blank - Different.esl", &load_order.game_settings()).unwrap();
1✔
393

1✔
394
        let position = load_order.insert_position(&plugin);
1✔
395

1✔
396
        assert_eq!(2, position.unwrap());
1✔
397
    }
1✔
398

399
    #[test]
1✔
400
    fn load_should_reload_existing_plugins() {
1✔
401
        let tmp_dir = tempdir().unwrap();
1✔
402
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
403

1✔
404
        assert!(!load_order.plugins()[1].is_master_file());
1✔
405
        copy_to_test_dir("Blank.esm", "Blank.esp", &load_order.game_settings());
1✔
406
        let plugin_path = load_order
1✔
407
            .game_settings()
1✔
408
            .plugins_directory()
1✔
409
            .join("Blank.esp");
1✔
410
        set_file_times(&plugin_path, FileTime::zero(), FileTime::zero()).unwrap();
1✔
411

1✔
412
        load_order.load().unwrap();
1✔
413

1✔
414
        assert!(load_order.plugins()[1].is_master_file());
1✔
415
    }
1✔
416

417
    #[test]
1✔
418
    fn load_should_remove_plugins_that_fail_to_load() {
1✔
419
        let tmp_dir = tempdir().unwrap();
1✔
420
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
421

1✔
422
        assert!(load_order.index_of("Blank.esp").is_some());
1✔
423
        assert!(load_order.index_of("Blank - Different.esp").is_some());
1✔
424

425
        let plugin_path = load_order
1✔
426
            .game_settings()
1✔
427
            .plugins_directory()
1✔
428
            .join("Blank.esp");
1✔
429
        File::create(&plugin_path).unwrap();
1✔
430
        set_file_times(&plugin_path, FileTime::zero(), FileTime::zero()).unwrap();
1✔
431

1✔
432
        let plugin_path = load_order
1✔
433
            .game_settings()
1✔
434
            .plugins_directory()
1✔
435
            .join("Blank - Different.esp");
1✔
436
        File::create(&plugin_path).unwrap();
1✔
437
        set_file_times(&plugin_path, FileTime::zero(), FileTime::zero()).unwrap();
1✔
438

1✔
439
        load_order.load().unwrap();
1✔
440
        assert!(load_order.index_of("Blank.esp").is_none());
1✔
441
        assert!(load_order.index_of("Blank - Different.esp").is_none());
1✔
442
    }
1✔
443

444
    #[test]
1✔
445
    fn load_should_get_load_order_from_active_plugins_file() {
1✔
446
        let tmp_dir = tempdir().unwrap();
1✔
447
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
448

1✔
449
        write_active_plugins_file(
1✔
450
            load_order.game_settings(),
1✔
451
            &["Blank.esp", "Blank - Master Dependent.esp"],
1✔
452
        );
1✔
453

1✔
454
        load_order.load().unwrap();
1✔
455

1✔
456
        let expected_filenames = vec![
1✔
457
            load_order.game_settings().master_file(),
1✔
458
            "Blank.esm",
1✔
459
            "Blank.esp",
1✔
460
            "Blank - Master Dependent.esp",
1✔
461
            "Blank - Different.esp",
1✔
462
            "Blàñk.esp",
1✔
463
        ];
1✔
464

1✔
465
        assert_eq!(expected_filenames, load_order.plugin_names());
1✔
466
    }
1✔
467

468
    #[test]
1✔
469
    fn load_should_hoist_non_masters_that_masters_depend_on_to_load_before_their_dependents() {
1✔
470
        let tmp_dir = tempdir().unwrap();
1✔
471
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
472

1✔
473
        // .esm plugins are loaded as ESMs, .esl plugins are loaded as ESMs and
1✔
474
        // ESLs, ignoring their actual flags, so only worth testing a .esp that
1✔
475
        // has the ESM flag set that has another (normal) .esp as a master.
1✔
476

1✔
477
        let plugins_dir = &load_order.game_settings().plugins_directory();
1✔
478
        copy_to_test_dir(
1✔
479
            "Blank - Plugin Dependent.esp",
1✔
480
            "Blank - Plugin Dependent.esp",
1✔
481
            load_order.game_settings(),
1✔
482
        );
1✔
483
        set_master_flag(&plugins_dir.join("Blank - Plugin Dependent.esp"), true).unwrap();
1✔
484

1✔
485
        let expected_filenames = vec![
1✔
486
            "Blank - Master Dependent.esp",
1✔
487
            "Blank - Different.esp",
1✔
488
            "Blàñk.esp",
1✔
489
            "Blank.esp",
1✔
490
            "Skyrim.esm",
1✔
491
            "Blank - Plugin Dependent.esp",
1✔
492
            "Blank.esm",
1✔
493
        ];
1✔
494
        write_active_plugins_file(load_order.game_settings(), &expected_filenames);
1✔
495

1✔
496
        load_order.load().unwrap();
1✔
497

1✔
498
        let expected_filenames = vec![
1✔
499
            "Skyrim.esm",
1✔
500
            "Blank.esp",
1✔
501
            "Blank - Plugin Dependent.esp",
1✔
502
            "Blank.esm",
1✔
503
            "Blank - Master Dependent.esp",
1✔
504
            "Blank - Different.esp",
1✔
505
            "Blàñk.esp",
1✔
506
        ];
1✔
507

1✔
508
        assert_eq!(expected_filenames, load_order.plugin_names());
1✔
509
    }
1✔
510

511
    #[test]
1✔
512
    fn load_should_decode_active_plugins_file_from_windows_1252() {
1✔
513
        let tmp_dir = tempdir().unwrap();
1✔
514
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
515

1✔
516
        write_active_plugins_file(load_order.game_settings(), &["Blàñk.esp", "Blank.esm"]);
1✔
517

1✔
518
        load_order.load().unwrap();
1✔
519

1✔
520
        let expected_filenames = vec![
1✔
521
            load_order.game_settings().master_file(),
1✔
522
            "Blank.esm",
1✔
523
            "Blàñk.esp",
1✔
524
            "Blank - Different.esp",
1✔
525
            "Blank - Master Dependent.esp",
1✔
526
            "Blank.esp",
1✔
527
        ];
1✔
528

1✔
529
        assert_eq!(expected_filenames, load_order.plugin_names());
1✔
530
    }
1✔
531

532
    #[test]
1✔
533
    fn load_should_handle_crlf_and_lf_in_active_plugins_file() {
1✔
534
        let tmp_dir = tempdir().unwrap();
1✔
535
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
536

1✔
537
        write_active_plugins_file(load_order.game_settings(), &["Blàñk.esp", "Blank.esm\r"]);
1✔
538

1✔
539
        load_order.load().unwrap();
1✔
540

1✔
541
        let expected_filenames = vec![
1✔
542
            load_order.game_settings().master_file(),
1✔
543
            "Blank.esm",
1✔
544
            "Blàñk.esp",
1✔
545
            "Blank - Different.esp",
1✔
546
            "Blank - Master Dependent.esp",
1✔
547
            "Blank.esp",
1✔
548
        ];
1✔
549

1✔
550
        assert_eq!(expected_filenames, load_order.plugin_names());
1✔
551
    }
1✔
552

553
    #[test]
1✔
554
    fn load_should_ignore_active_plugins_file_lines_starting_with_a_hash() {
1✔
555
        let tmp_dir = tempdir().unwrap();
1✔
556
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
557

1✔
558
        write_active_plugins_file(
1✔
559
            load_order.game_settings(),
1✔
560
            &["#Blank.esp", "Blàñk.esp", "Blank.esm"],
1✔
561
        );
1✔
562

1✔
563
        load_order.load().unwrap();
1✔
564

1✔
565
        let expected_filenames = vec![
1✔
566
            load_order.game_settings().master_file(),
1✔
567
            "Blank.esm",
1✔
568
            "Blàñk.esp",
1✔
569
            "Blank - Different.esp",
1✔
570
            "Blank - Master Dependent.esp",
1✔
571
            "Blank.esp",
1✔
572
        ];
1✔
573

1✔
574
        assert_eq!(expected_filenames, load_order.plugin_names());
1✔
575
    }
1✔
576

577
    #[test]
1✔
578
    fn load_should_ignore_plugins_in_active_plugins_file_that_are_not_installed() {
1✔
579
        let tmp_dir = tempdir().unwrap();
1✔
580
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
581

1✔
582
        write_active_plugins_file(
1✔
583
            load_order.game_settings(),
1✔
584
            &["Blàñk.esp", "Blank.esm", "missing.esp"],
1✔
585
        );
1✔
586

1✔
587
        load_order.load().unwrap();
1✔
588

1✔
589
        let expected_filenames = vec![
1✔
590
            load_order.game_settings().master_file(),
1✔
591
            "Blank.esm",
1✔
592
            "Blàñk.esp",
1✔
593
            "Blank - Different.esp",
1✔
594
            "Blank - Master Dependent.esp",
1✔
595
            "Blank.esp",
1✔
596
        ];
1✔
597

1✔
598
        assert_eq!(expected_filenames, load_order.plugin_names());
1✔
599
    }
1✔
600

601
    #[test]
1✔
602
    fn load_should_add_missing_plugins() {
1✔
603
        let tmp_dir = tempdir().unwrap();
1✔
604
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
605

1✔
606
        assert!(load_order.index_of("Blank.esm").is_none());
1✔
607
        assert!(load_order
1✔
608
            .index_of("Blank - Master Dependent.esp")
1✔
609
            .is_none());
1✔
610
        assert!(load_order.index_of("Blàñk.esp").is_none());
1✔
611

612
        load_order.load().unwrap();
1✔
613

1✔
614
        assert!(load_order.index_of("Blank.esm").is_some());
1✔
615
        assert!(load_order
1✔
616
            .index_of("Blank - Master Dependent.esp")
1✔
617
            .is_some());
1✔
618
        assert!(load_order.index_of("Blàñk.esp").is_some());
1✔
619
    }
1✔
620

621
    #[test]
1✔
622
    fn load_should_recognise_light_master_plugins() {
1✔
623
        let tmp_dir = tempdir().unwrap();
1✔
624
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
625

1✔
626
        copy_to_test_dir("Blank.esm", "ccTest.esl", &load_order.game_settings());
1✔
627

1✔
628
        load_order.load().unwrap();
1✔
629

1✔
630
        assert!(load_order.plugin_names().contains(&"ccTest.esl"));
1✔
631
    }
1✔
632

633
    #[test]
1✔
634
    fn load_should_add_missing_implicitly_active_plugins_in_their_hardcoded_positions() {
1✔
635
        let tmp_dir = tempdir().unwrap();
1✔
636
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
637

1✔
638
        copy_to_test_dir("Blank.esm", "Update.esm", &load_order.game_settings());
1✔
639
        load_order.load().unwrap();
1✔
640
        assert_eq!(Some(1), load_order.index_of("Update.esm"));
1✔
641
        assert!(load_order.is_active("Update.esm"));
1✔
642
    }
1✔
643

644
    #[test]
1✔
645
    fn load_should_empty_the_load_order_if_the_plugins_directory_does_not_exist() {
1✔
646
        let tmp_dir = tempdir().unwrap();
1✔
647
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
648
        tmp_dir.close().unwrap();
1✔
649

1✔
650
        load_order.load().unwrap();
1✔
651

1✔
652
        assert!(load_order.plugins().is_empty());
1✔
653
    }
1✔
654

655
    #[test]
1✔
656
    fn load_should_load_plugin_states_from_active_plugins_file() {
1✔
657
        let tmp_dir = tempdir().unwrap();
1✔
658
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
659

1✔
660
        write_active_plugins_file(load_order.game_settings(), &["Blàñk.esp", "Blank.esm"]);
1✔
661

1✔
662
        load_order.load().unwrap();
1✔
663
        let expected_filenames = vec!["Skyrim.esm", "Blank.esm", "Blàñk.esp"];
1✔
664

1✔
665
        assert_eq!(expected_filenames, load_order.active_plugin_names());
1✔
666
    }
1✔
667

668
    #[test]
1✔
669
    fn load_should_succeed_when_active_plugins_file_is_missing() {
1✔
670
        let tmp_dir = tempdir().unwrap();
1✔
671
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
672

1✔
673
        assert!(load_order.load().is_ok());
1✔
674
        assert_eq!(1, load_order.active_plugin_names().len());
1✔
675
    }
1✔
676

677
    #[test]
1✔
678
    fn load_should_not_duplicate_a_plugin_that_has_a_ghosted_duplicate() {
1✔
679
        let tmp_dir = tempdir().unwrap();
1✔
680
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
681

1✔
682
        use std::fs::copy;
1✔
683

1✔
684
        copy(
1✔
685
            load_order
1✔
686
                .game_settings()
1✔
687
                .plugins_directory()
1✔
688
                .join("Blank.esm"),
1✔
689
            load_order
1✔
690
                .game_settings()
1✔
691
                .plugins_directory()
1✔
692
                .join("Blank.esm.ghost"),
1✔
693
        )
1✔
694
        .unwrap();
1✔
695

1✔
696
        load_order.load().unwrap();
1✔
697

1✔
698
        let expected_filenames = vec![
1✔
699
            load_order.game_settings().master_file(),
1✔
700
            "Blank.esm",
1✔
701
            "Blank - Different.esp",
1✔
702
            "Blank - Master Dependent.esp",
1✔
703
            "Blank.esp",
1✔
704
            "Blàñk.esp",
1✔
705
        ];
1✔
706

1✔
707
        assert_eq!(expected_filenames, load_order.plugin_names());
1✔
708
    }
1✔
709

710
    #[test]
1✔
711
    fn load_should_not_move_light_master_esp_files_before_non_masters() {
1✔
712
        let tmp_dir = tempdir().unwrap();
1✔
713
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
714

1✔
715
        copy_to_test_dir("Blank.esl", "Blank.esl.esp", &load_order.game_settings());
1✔
716

1✔
717
        load_order.load().unwrap();
1✔
718

1✔
719
        let expected_filenames = vec![
1✔
720
            load_order.game_settings().master_file(),
1✔
721
            "Blank.esm",
1✔
722
            "Blank - Different.esp",
1✔
723
            "Blank - Master Dependent.esp",
1✔
724
            "Blank.esl.esp",
1✔
725
            "Blank.esp",
1✔
726
            "Blàñk.esp",
1✔
727
        ];
1✔
728

1✔
729
        assert_eq!(expected_filenames, load_order.plugin_names());
1✔
730
    }
1✔
731

732
    #[test]
1✔
733
    fn load_should_add_plugins_in_additional_plugins_directory_before_those_in_main_plugins_directory(
1✔
734
    ) {
1✔
735
        let tmp_dir = tempdir().unwrap();
1✔
736
        let game_path = tmp_dir.path().join("Fallout 4/Content");
1✔
737
        create_dir_all(&game_path).unwrap();
1✔
738

1✔
739
        File::create(game_path.join("appxmanifest.xml")).unwrap();
1✔
740

1✔
741
        let mut load_order = prepare(GameId::Fallout4, &game_path);
1✔
742

1✔
743
        copy_to_test_dir("Blank.esm", "Blank.esm", &load_order.game_settings());
1✔
744

1✔
745
        let dlc_path = tmp_dir
1✔
746
            .path()
1✔
747
            .join("Fallout 4- Far Harbor (PC)/Content/Data");
1✔
748
        create_dir_all(&dlc_path).unwrap();
1✔
749
        copy_to_dir(
1✔
750
            "Blank.esm",
1✔
751
            &dlc_path,
1✔
752
            "DLCCoast.esm",
1✔
753
            &load_order.game_settings(),
1✔
754
        );
1✔
755
        copy_to_dir(
1✔
756
            "Blank.esp",
1✔
757
            &dlc_path,
1✔
758
            "Blank DLC.esp",
1✔
759
            &load_order.game_settings(),
1✔
760
        );
1✔
761

1✔
762
        load_order.load().unwrap();
1✔
763

1✔
764
        let expected_filenames = vec![
1✔
765
            "Fallout4.esm",
1✔
766
            "DLCCoast.esm",
1✔
767
            "Blank.esm",
1✔
768
            "Blank - Different.esp",
1✔
769
            "Blank - Master Dependent.esp",
1✔
770
            "Blank DLC.esp",
1✔
771
            "Blank.esp",
1✔
772
            "Blàñk.esp",
1✔
773
        ];
1✔
774

1✔
775
        assert_eq!(expected_filenames, load_order.plugin_names());
1✔
776
    }
1✔
777

778
    #[test]
1✔
779
    fn save_should_create_active_plugins_file_parent_directory_if_it_does_not_exist() {
1✔
780
        let tmp_dir = tempdir().unwrap();
1✔
781
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
782

1✔
783
        remove_dir_all(
1✔
784
            load_order
1✔
785
                .game_settings()
1✔
786
                .active_plugins_file()
1✔
787
                .parent()
1✔
788
                .unwrap(),
1✔
789
        )
1✔
790
        .unwrap();
1✔
791

1✔
792
        load_order.save().unwrap();
1✔
793

1✔
794
        assert!(load_order
1✔
795
            .game_settings()
1✔
796
            .active_plugins_file()
1✔
797
            .parent()
1✔
798
            .unwrap()
1✔
799
            .exists());
1✔
800
    }
1✔
801

802
    #[test]
1✔
803
    fn save_should_write_active_plugins_file() {
1✔
804
        let tmp_dir = tempdir().unwrap();
1✔
805
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
806

1✔
807
        load_order.save().unwrap();
1✔
808

1✔
809
        load_order.load().unwrap();
1✔
810
        assert_eq!(
1✔
811
            vec!["Skyrim.esm", "Blank.esp"],
1✔
812
            load_order.active_plugin_names()
1✔
813
        );
1✔
814
    }
1✔
815

816
    #[test]
1✔
817
    fn save_should_write_unghosted_plugin_names() {
1✔
818
        let tmp_dir = tempdir().unwrap();
1✔
819
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
820

1✔
821
        copy_to_test_dir(
1✔
822
            "Blank - Different.esm",
1✔
823
            "ghosted.esm.ghost",
1✔
824
            &load_order.game_settings(),
1✔
825
        );
1✔
826
        let plugin = Plugin::new("ghosted.esm.ghost", &load_order.game_settings()).unwrap();
1✔
827
        load_order.plugins_mut().push(plugin);
1✔
828

1✔
829
        load_order.save().unwrap();
1✔
830

1✔
831
        let reader =
1✔
832
            BufReader::new(File::open(load_order.game_settings().active_plugins_file()).unwrap());
1✔
833

1✔
834
        let lines = reader
1✔
835
            .lines()
1✔
836
            .collect::<Result<Vec<String>, io::Error>>()
1✔
837
            .unwrap();
1✔
838

1✔
839
        assert_eq!(
1✔
840
            vec!["*Blank.esp", "Blank - Different.esp", "ghosted.esm"],
1✔
841
            lines
1✔
842
        );
1✔
843
    }
1✔
844

845
    #[test]
1✔
846
    fn save_should_error_if_a_plugin_filename_cannot_be_encoded_in_windows_1252() {
1✔
847
        let tmp_dir = tempdir().unwrap();
1✔
848
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
849

1✔
850
        let filename = "Bl\u{0227}nk.esm";
1✔
851
        copy_to_test_dir(
1✔
852
            "Blank - Different.esm",
1✔
853
            filename,
1✔
854
            &load_order.game_settings(),
1✔
855
        );
1✔
856
        let plugin = Plugin::new(filename, &load_order.game_settings()).unwrap();
1✔
857
        load_order.plugins_mut().push(plugin);
1✔
858

1✔
859
        match load_order.save().unwrap_err() {
1✔
860
            Error::EncodeError(s) => assert_eq!("unrepresentable character", s),
1✔
861
            e => panic!("Expected encode error, got {:?}", e),
×
862
        };
863
    }
1✔
864

865
    #[test]
1✔
866
    fn set_load_order_should_error_if_given_an_empty_list() {
1✔
867
        let tmp_dir = tempdir().unwrap();
1✔
868
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
869

1✔
870
        let existing_filenames = to_owned(load_order.plugin_names());
1✔
871
        let filenames = vec![];
1✔
872
        assert!(load_order.set_load_order(&filenames).is_err());
1✔
873
        assert_eq!(existing_filenames, load_order.plugin_names());
1✔
874
    }
1✔
875

876
    #[test]
1✔
877
    fn set_load_order_should_error_if_the_first_element_given_is_not_the_game_master() {
1✔
878
        let tmp_dir = tempdir().unwrap();
1✔
879
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
880

1✔
881
        let existing_filenames = to_owned(load_order.plugin_names());
1✔
882
        let filenames = vec!["Blank.esp"];
1✔
883
        assert!(load_order.set_load_order(&filenames).is_err());
1✔
884
        assert_eq!(existing_filenames, load_order.plugin_names());
1✔
885
    }
1✔
886

887
    #[test]
1✔
888
    fn set_load_order_should_error_if_an_implicitly_active_plugin_loads_after_another_plugin() {
1✔
889
        let tmp_dir = tempdir().unwrap();
1✔
890
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
891

1✔
892
        copy_to_test_dir("Blank.esm", "Update.esm", &load_order.game_settings());
1✔
893

1✔
894
        let filenames = vec![
1✔
895
            "Skyrim.esm",
1✔
896
            "Blank.esm",
1✔
897
            "Update.esm",
1✔
898
            "Blank.esp",
1✔
899
            "Blank - Master Dependent.esp",
1✔
900
            "Blank - Different.esp",
1✔
901
            "Blàñk.esp",
1✔
902
        ];
1✔
903

1✔
904
        match load_order.set_load_order(&filenames).unwrap_err() {
1✔
905
            Error::GameMasterMustLoadFirst => {}
1✔
906
            e => panic!("Wrong error type: {:?}", e),
×
907
        }
908
    }
1✔
909

910
    #[test]
1✔
911
    fn set_load_order_should_not_error_if_an_implicitly_active_plugin_is_missing() {
1✔
912
        let tmp_dir = tempdir().unwrap();
1✔
913
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
914

1✔
915
        copy_to_test_dir("Blank.esm", "Dragonborn.esm", &load_order.game_settings());
1✔
916

1✔
917
        let filenames = vec![
1✔
918
            "Skyrim.esm",
1✔
919
            "Dragonborn.esm",
1✔
920
            "Blank.esm",
1✔
921
            "Blank.esp",
1✔
922
            "Blank - Master Dependent.esp",
1✔
923
            "Blank - Different.esp",
1✔
924
            "Blàñk.esp",
1✔
925
        ];
1✔
926

1✔
927
        assert!(load_order.set_load_order(&filenames).is_ok());
1✔
928
    }
1✔
929

930
    #[test]
1✔
931
    fn set_load_order_should_not_distinguish_between_ghosted_and_unghosted_filenames() {
1✔
932
        let tmp_dir = tempdir().unwrap();
1✔
933
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
934

1✔
935
        copy_to_test_dir(
1✔
936
            "Blank - Different.esm",
1✔
937
            "ghosted.esm.ghost",
1✔
938
            &load_order.game_settings(),
1✔
939
        );
1✔
940

1✔
941
        let filenames = vec![
1✔
942
            "Skyrim.esm",
1✔
943
            "Blank.esm",
1✔
944
            "ghosted.esm",
1✔
945
            "Blank.esp",
1✔
946
            "Blank - Master Dependent.esp",
1✔
947
            "Blank - Different.esp",
1✔
948
            "Blàñk.esp",
1✔
949
        ];
1✔
950

1✔
951
        assert!(load_order.set_load_order(&filenames).is_ok());
1✔
952
    }
1✔
953

954
    #[test]
1✔
955
    fn set_load_order_should_not_insert_missing_plugins() {
1✔
956
        let tmp_dir = tempdir().unwrap();
1✔
957
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
958

1✔
959
        let filenames = vec![
1✔
960
            "Skyrim.esm",
1✔
961
            "Blank.esm",
1✔
962
            "Blank.esp",
1✔
963
            "Blank - Master Dependent.esp",
1✔
964
            "Blank - Different.esp",
1✔
965
        ];
1✔
966
        load_order.set_load_order(&filenames).unwrap();
1✔
967

1✔
968
        assert_eq!(filenames, load_order.plugin_names());
1✔
969
    }
1✔
970

971
    #[test]
1✔
972
    fn set_load_order_should_not_lose_active_state_of_existing_plugins() {
1✔
973
        let tmp_dir = tempdir().unwrap();
1✔
974
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
975

1✔
976
        let filenames = vec![
1✔
977
            "Skyrim.esm",
1✔
978
            "Blank.esm",
1✔
979
            "Blank.esp",
1✔
980
            "Blank - Master Dependent.esp",
1✔
981
            "Blank - Different.esp",
1✔
982
        ];
1✔
983
        load_order.set_load_order(&filenames).unwrap();
1✔
984

1✔
985
        assert!(load_order.is_active("Blank.esp"));
1✔
986
    }
1✔
987

988
    #[test]
1✔
989
    fn set_plugin_index_should_error_if_setting_the_game_master_index_to_non_zero_in_bounds() {
1✔
990
        let tmp_dir = tempdir().unwrap();
1✔
991
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
992

1✔
993
        let existing_filenames = to_owned(load_order.plugin_names());
1✔
994
        assert!(load_order.set_plugin_index("Skyrim.esm", 1).is_err());
1✔
995
        assert_eq!(existing_filenames, load_order.plugin_names());
1✔
996
    }
1✔
997

998
    #[test]
1✔
999
    fn set_plugin_index_should_error_if_setting_a_zero_index_for_a_non_game_master_plugin() {
1✔
1000
        let tmp_dir = tempdir().unwrap();
1✔
1001
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
1002

1✔
1003
        let existing_filenames = to_owned(load_order.plugin_names());
1✔
1004
        assert!(load_order.set_plugin_index("Blank.esm", 0).is_err());
1✔
1005
        assert_eq!(existing_filenames, load_order.plugin_names());
1✔
1006
    }
1✔
1007

1008
    #[test]
1✔
1009
    fn set_plugin_index_should_insert_a_new_plugin() {
1✔
1010
        let tmp_dir = tempdir().unwrap();
1✔
1011
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
1012

1✔
1013
        let num_plugins = load_order.plugins().len();
1✔
1014
        assert_eq!(1, load_order.set_plugin_index("Blank.esm", 1).unwrap());
1✔
1015
        assert_eq!(1, load_order.index_of("Blank.esm").unwrap());
1✔
1016
        assert_eq!(num_plugins + 1, load_order.plugins().len());
1✔
1017
    }
1✔
1018

1019
    #[test]
1✔
1020
    fn is_self_consistent_should_return_true() {
1✔
1021
        let tmp_dir = tempdir().unwrap();
1✔
1022
        let load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
1023

1✔
1024
        assert!(load_order.is_self_consistent().unwrap());
1✔
1025
    }
1✔
1026

1027
    #[test]
1✔
1028
    fn is_ambiguous_should_return_false_if_all_loaded_plugins_are_listed_in_active_plugins_file() {
1✔
1029
        let tmp_dir = tempdir().unwrap();
1✔
1030
        let load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
1031

1✔
1032
        let loaded_plugin_names: Vec<&str> = load_order
1✔
1033
            .plugins
1✔
1034
            .iter()
1✔
1035
            .map(|plugin| plugin.name())
3✔
1036
            .collect();
1✔
1037
        write_active_plugins_file(load_order.game_settings(), &loaded_plugin_names);
1✔
1038

1✔
1039
        assert!(!load_order.is_ambiguous().unwrap());
1✔
1040
    }
1✔
1041

1042
    #[test]
1✔
1043
    fn is_ambiguous_should_ignore_plugins_that_are_listed_in_active_plugins_file_but_not_loaded() {
1✔
1044
        let tmp_dir = tempdir().unwrap();
1✔
1045
        let load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
1046

1✔
1047
        assert!(load_order.index_of("missing.esp").is_none());
1✔
1048

1049
        let mut loaded_plugin_names: Vec<&str> = load_order
1✔
1050
            .plugins
1✔
1051
            .iter()
1✔
1052
            .map(|plugin| plugin.name())
3✔
1053
            .collect();
1✔
1054
        loaded_plugin_names.push("missing.esp");
1✔
1055

1✔
1056
        write_active_plugins_file(load_order.game_settings(), &loaded_plugin_names);
1✔
1057

1✔
1058
        assert!(!load_order.is_ambiguous().unwrap());
1✔
1059
    }
1✔
1060

1061
    #[test]
1✔
1062
    fn is_ambiguous_should_ignore_loaded_implicitly_active_plugins_not_listed_in_active_plugins_file(
1✔
1063
    ) {
1✔
1064
        let tmp_dir = tempdir().unwrap();
1✔
1065
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
1066

1✔
1067
        let loaded_plugin_names: Vec<&str> = load_order
1✔
1068
            .plugins
1✔
1069
            .iter()
1✔
1070
            .map(|plugin| plugin.name())
3✔
1071
            .collect();
1✔
1072

1✔
1073
        write_active_plugins_file(load_order.game_settings(), &loaded_plugin_names);
1✔
1074

1✔
1075
        copy_to_test_dir("Blank.esm", "Dawnguard.esm", &load_order.game_settings());
1✔
1076
        let plugin = Plugin::new("Dawnguard.esm", &load_order.game_settings()).unwrap();
1✔
1077
        load_order.plugins_mut().push(plugin);
1✔
1078

1✔
1079
        assert!(!load_order.is_ambiguous().unwrap());
1✔
1080
    }
1✔
1081

1082
    #[test]
1✔
1083
    fn is_ambiguous_should_return_true_if_there_are_loaded_plugins_not_in_active_plugins_file() {
1✔
1084
        let tmp_dir = tempdir().unwrap();
1✔
1085
        let load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
1086

1✔
1087
        let mut loaded_plugin_names: Vec<&str> = load_order
1✔
1088
            .plugins
1✔
1089
            .iter()
1✔
1090
            .map(|plugin| plugin.name())
3✔
1091
            .collect();
1✔
1092

1✔
1093
        loaded_plugin_names.pop();
1✔
1094

1✔
1095
        write_active_plugins_file(load_order.game_settings(), &loaded_plugin_names);
1✔
1096

1✔
1097
        assert!(load_order.is_ambiguous().unwrap());
1✔
1098
    }
1✔
1099

1100
    #[test]
1✔
1101
    fn activate_should_check_normal_plugins_and_light_masters_active_limits_separately() {
1✔
1102
        let tmp_dir = tempdir().unwrap();
1✔
1103
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
1104

1✔
1105
        let plugins = prepare_bulk_plugins(load_order.game_settings());
1✔
1106

1✔
1107
        let mut plugin_refs: Vec<&str> = plugins[..254].iter().map(AsRef::as_ref).collect();
1✔
1108
        plugin_refs.extend(plugins[261..4356].iter().map(|s| s.as_str()));
4,095✔
1109

1✔
1110
        load_order.load().unwrap();
1✔
1111
        assert!(load_order.set_active_plugins(&plugin_refs).is_ok());
1✔
1112

1113
        let i = 4356;
1✔
1114
        assert!(load_order.activate(&plugins[i]).is_ok());
1✔
1115
        assert!(load_order.is_active(&plugins[i]));
1✔
1116

1117
        let i = 254;
1✔
1118
        assert!(load_order.activate(&plugins[i]).is_ok());
1✔
1119
        assert!(load_order.is_active(&plugins[i]));
1✔
1120

1121
        let i = 256;
1✔
1122
        assert!(load_order.activate(&plugins[i]).is_err());
1✔
1123
        assert!(!load_order.is_active(&plugins[i]));
1✔
1124

1125
        let i = 4357;
1✔
1126
        assert!(load_order.activate(&plugins[i]).is_err());
1✔
1127
        assert!(!load_order.is_active(&plugins[i]));
1✔
1128
    }
1✔
1129

1130
    #[test]
1✔
1131
    fn set_active_plugins_should_count_light_masters_and_normal_plugins_separately() {
1✔
1132
        let tmp_dir = tempdir().unwrap();
1✔
1133
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
1134

1✔
1135
        let plugins = prepare_bulk_plugins(load_order.game_settings());
1✔
1136

1✔
1137
        let mut plugin_refs: Vec<&str> = plugins[..255].iter().map(AsRef::as_ref).collect();
1✔
1138
        plugin_refs.extend(plugins[261..4357].iter().map(|s| s.as_str()));
4,096✔
1139

1✔
1140
        load_order.load().unwrap();
1✔
1141
        assert!(load_order.set_active_plugins(&plugin_refs).is_ok());
1✔
1142
        assert_eq!(4351, load_order.active_plugin_names().len());
1✔
1143
    }
1✔
1144

1145
    #[test]
1✔
1146
    fn set_active_plugins_should_error_if_given_more_than_4096_light_masters() {
1✔
1147
        let tmp_dir = tempdir().unwrap();
1✔
1148
        let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());
1✔
1149

1✔
1150
        let plugins = prepare_bulk_plugins(load_order.game_settings());
1✔
1151

1✔
1152
        let mut plugin_refs: Vec<&str> = plugins[..255].iter().map(AsRef::as_ref).collect();
1✔
1153
        plugin_refs.extend(plugins[261..4358].iter().map(|s| s.as_str()));
4,097✔
1154

1✔
1155
        assert!(load_order.set_active_plugins(&plugin_refs).is_err());
1✔
1156
        assert_eq!(1, load_order.active_plugin_names().len());
1✔
1157
    }
1✔
1158
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc