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

Ortham / libloadorder / 9669712859

25 Jun 2024 09:15PM UTC coverage: 91.599% (+4.7%) from 86.942%
9669712859

push

github

Ortham
Use Starfield plugins in Starfield tests

65 of 65 new or added lines in 5 files covered. (100.0%)

163 existing lines in 11 files now uncovered.

7218 of 7880 relevant lines covered (91.6%)

66616.03 hits per line

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

98.42
/src/load_order/writable.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::create_dir_all;
21
use std::path::Path;
22

23
use unicase::{eq, UniCase};
24

25
use super::mutable::MutableLoadOrder;
26
use super::readable::{ReadableLoadOrder, ReadableLoadOrderBase};
27
use crate::enums::Error;
28
use crate::plugin::Plugin;
29
use crate::GameSettings;
30

31
const MAX_ACTIVE_NORMAL_PLUGINS: usize = 255;
32
const MAX_ACTIVE_LIGHT_PLUGINS: usize = 4096;
33

34
pub trait WritableLoadOrder: ReadableLoadOrder {
35
    fn game_settings_mut(&mut self) -> &mut GameSettings;
36

37
    fn load(&mut self) -> Result<(), Error>;
38

39
    fn save(&mut self) -> Result<(), Error>;
40

41
    fn add(&mut self, plugin_name: &str) -> Result<usize, Error>;
42

43
    fn remove(&mut self, plugin_name: &str) -> Result<(), Error>;
44

45
    fn set_load_order(&mut self, plugin_names: &[&str]) -> Result<(), Error>;
46

47
    fn set_plugin_index(&mut self, plugin_name: &str, position: usize) -> Result<usize, Error>;
48

49
    fn is_self_consistent(&self) -> Result<bool, Error>;
50

51
    fn is_ambiguous(&self) -> Result<bool, Error>;
52

53
    fn activate(&mut self, plugin_name: &str) -> Result<(), Error>;
54

55
    fn deactivate(&mut self, plugin_name: &str) -> Result<(), Error>;
56

57
    fn set_active_plugins(&mut self, active_plugin_names: &[&str]) -> Result<(), Error>;
58
}
59

60
pub fn add<T: MutableLoadOrder>(load_order: &mut T, plugin_name: &str) -> Result<usize, Error> {
12✔
61
    match load_order.index_of(plugin_name) {
12✔
62
        Some(_) => Err(Error::DuplicatePlugin(plugin_name.to_string())),
1✔
63
        None => {
64
            let plugin = Plugin::new(plugin_name, load_order.game_settings())?;
11✔
65

66
            match load_order.insert_position(&plugin) {
10✔
67
                Some(position) => {
8✔
68
                    load_order.validate_index(&plugin, position)?;
8✔
69
                    load_order.plugins_mut().insert(position, plugin);
7✔
70
                    Ok(position)
7✔
71
                }
72
                None => {
73
                    load_order.validate_index(&plugin, load_order.plugins().len())?;
2✔
74
                    load_order.plugins_mut().push(plugin);
2✔
75
                    Ok(load_order.plugins().len() - 1)
2✔
76
                }
77
            }
78
        }
79
    }
80
}
12✔
81

82
pub fn remove<T: MutableLoadOrder>(load_order: &mut T, plugin_name: &str) -> Result<(), Error> {
4✔
83
    match load_order.index_of(plugin_name) {
4✔
84
        Some(index) => {
3✔
85
            let plugin_path = load_order.game_settings().plugin_path(plugin_name);
3✔
86
            if plugin_path.exists() {
3✔
87
                return Err(Error::InstalledPlugin(plugin_name.to_string()));
1✔
88
            }
2✔
89

2✔
90
            // If this is a master file that depends on a non-master file, it shouldn't be removed
2✔
91
            // without first moving the non-master file later in the load order, unless the next
2✔
92
            // master file also depends on that same non-master file. The non-master file also
2✔
93
            // doesn't need to be moved if this is the last master file in the load order.
2✔
94
            if load_order.plugins()[index].is_master_file() {
2✔
95
                let next_master_index = &load_order
1✔
96
                    .plugins()
1✔
97
                    .iter()
1✔
98
                    .skip(index + 1)
1✔
99
                    .position(|p| p.is_master_file());
1✔
100

101
                if let Some(next_master_index) = next_master_index {
1✔
102
                    let next_master_masters = load_order.plugins()[*next_master_index].masters()?;
1✔
103
                    let next_master_master_names: HashSet<_> =
1✔
104
                        next_master_masters.iter().map(UniCase::new).collect();
1✔
105

106
                    let mut masters = load_order.plugins()[index].masters()?;
1✔
107

108
                    // Remove any masters that are also masters of the next master plugin.
109
                    masters.retain(|m| !next_master_master_names.contains(&UniCase::new(m)));
1✔
110

111
                    // Finally, check if any remaining masters are non-master plugins.
112
                    if let Some(n) = masters.iter().find(|n| {
1✔
113
                        load_order
1✔
114
                            .index_of(n)
1✔
115
                            .map(|i| !load_order.plugins()[i].is_master_file())
1✔
116
                            // If the master isn't installed, assume it's a master file and so
1✔
117
                            // doesn't prevent removal of the target plugin.
1✔
118
                            .unwrap_or(false)
1✔
119
                    }) {
1✔
120
                        return Err(Error::NonMasterBeforeMaster {
1✔
121
                            master: plugin_name.to_string(),
1✔
122
                            non_master: n.to_string(),
1✔
123
                        });
1✔
UNCOV
124
                    }
×
UNCOV
125
                }
×
126
            }
1✔
127

128
            load_order.plugins_mut().remove(index);
1✔
129

1✔
130
            Ok(())
1✔
131
        }
132
        None => Err(Error::PluginNotFound(plugin_name.to_string())),
1✔
133
    }
134
}
4✔
135

136
#[derive(Clone, Copy, Debug, Default)]
137
struct PluginCounts {
138
    light: usize,
139
    normal: usize,
140
}
141

142
fn count_active_plugins<T: ReadableLoadOrderBase>(load_order: &T) -> PluginCounts {
1,029✔
143
    let mut counts = PluginCounts::default();
1,029✔
144

145
    for plugin in load_order.plugins().iter().filter(|p| p.is_active()) {
154,187✔
146
        if plugin.is_light_plugin() {
147,966✔
147
            counts.light += 1;
16,383✔
148
        } else if !plugin.is_override_plugin() {
131,583✔
149
            counts.normal += 1;
99,198✔
150
        }
99,198✔
151
    }
152

153
    counts
1,029✔
154
}
1,029✔
155

156
fn count_plugins(existing_plugins: &[Plugin], existing_plugin_indexes: &[usize]) -> PluginCounts {
6✔
157
    let mut counts = PluginCounts::default();
6✔
158

159
    for index in existing_plugin_indexes {
8,965✔
160
        let plugin = &existing_plugins[*index];
8,959✔
161

8,959✔
162
        if plugin.is_light_plugin() {
8,959✔
163
            counts.light += 1;
8,191✔
164
        } else if !plugin.is_override_plugin() {
8,191✔
165
            counts.normal += 1;
767✔
166
        }
767✔
167
    }
168

169
    counts
6✔
170
}
6✔
171

172
pub fn activate<T: MutableLoadOrder>(load_order: &mut T, plugin_name: &str) -> Result<(), Error> {
1,029✔
173
    let counts = count_active_plugins(load_order);
1,029✔
174

175
    let plugin = match load_order
1,029✔
176
        .plugins_mut()
1,029✔
177
        .iter_mut()
1,029✔
178
        .find(|p| p.name_matches(plugin_name))
141,839✔
179
    {
180
        Some(p) => p,
1,027✔
181
        None => return Err(Error::PluginNotFound(plugin_name.to_string())),
2✔
182
    };
183

184
    if !plugin.is_active() {
1,027✔
185
        let is_light = plugin.is_light_plugin();
1,026✔
186

1,026✔
187
        if (is_light && counts.light == MAX_ACTIVE_LIGHT_PLUGINS)
1,026✔
188
            || (!is_light
1,025✔
189
                && !plugin.is_override_plugin()
1,024✔
190
                && counts.normal == MAX_ACTIVE_NORMAL_PLUGINS)
768✔
191
        {
192
            return Err(Error::TooManyActivePlugins {
3✔
193
                light_count: counts.light,
3✔
194
                normal_count: counts.normal,
3✔
195
            });
3✔
196
        } else {
197
            plugin.activate()?;
1,023✔
198
        }
199
    }
1✔
200

201
    Ok(())
1,024✔
202
}
1,029✔
203

204
pub fn deactivate<T: MutableLoadOrder>(load_order: &mut T, plugin_name: &str) -> Result<(), Error> {
5✔
205
    if load_order.game_settings().is_implicitly_active(plugin_name) {
5✔
206
        return Err(Error::ImplicitlyActivePlugin(plugin_name.to_string()));
2✔
207
    }
3✔
208

3✔
209
    load_order
3✔
210
        .plugins_mut()
3✔
211
        .iter_mut()
3✔
212
        .find(|p| p.name_matches(plugin_name))
8✔
213
        .ok_or_else(|| Error::PluginNotFound(plugin_name.to_string()))
3✔
214
        .map(|p| p.deactivate())
3✔
215
}
5✔
216

217
pub fn set_active_plugins<T: MutableLoadOrder>(
11✔
218
    load_order: &mut T,
11✔
219
    active_plugin_names: &[&str],
11✔
220
) -> Result<(), Error> {
11✔
221
    let existing_plugin_indices = load_order.lookup_plugins(active_plugin_names)?;
11✔
222

223
    let counts = count_plugins(load_order.plugins(), &existing_plugin_indices);
6✔
224

6✔
225
    if counts.normal > MAX_ACTIVE_NORMAL_PLUGINS || counts.light > MAX_ACTIVE_LIGHT_PLUGINS {
6✔
UNCOV
226
        return Err(Error::TooManyActivePlugins {
×
UNCOV
227
            light_count: counts.light,
×
UNCOV
228
            normal_count: counts.normal,
×
UNCOV
229
        });
×
230
    }
6✔
231

232
    for plugin_name in load_order.game_settings().implicitly_active_plugins() {
15✔
233
        // If the plugin isn't installed, don't check that it's in the active
234
        // plugins list. Installed plugins will have already been loaded.
235
        if load_order.index_of(plugin_name).is_some()
15✔
236
            && !active_plugin_names.iter().any(|p| eq(*p, plugin_name))
4✔
237
        {
238
            return Err(Error::ImplicitlyActivePlugin(plugin_name.to_string()));
1✔
239
        }
14✔
240
    }
241

242
    load_order.deactivate_all();
5✔
243

244
    for index in existing_plugin_indices {
8,963✔
245
        load_order.plugins_mut()[index].activate()?;
8,958✔
246
    }
247

248
    Ok(())
5✔
249
}
11✔
250

251
pub fn create_parent_dirs(path: &Path) -> Result<(), Error> {
33✔
252
    if let Some(x) = path.parent() {
33✔
253
        if !x.exists() {
33✔
254
            create_dir_all(x).map_err(|e| Error::IoError(x.to_path_buf(), e))?
14✔
255
        }
19✔
UNCOV
256
    }
×
257
    Ok(())
33✔
258
}
33✔
259

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

264
    use std::fs::remove_file;
265
    use std::path::Path;
266

267
    use tempfile::tempdir;
268

269
    use crate::enums::GameId;
270
    use crate::game_settings::GameSettings;
271
    use crate::load_order::mutable::{generic_insert_position, MutableLoadOrder};
272
    use crate::load_order::readable::{ReadableLoadOrder, ReadableLoadOrderBase};
273
    use crate::load_order::tests::{load_and_insert, mock_game_files, set_master_flag};
274
    use crate::tests::copy_to_test_dir;
275

276
    struct TestLoadOrder {
277
        game_settings: GameSettings,
278
        plugins: Vec<Plugin>,
279
    }
280

281
    impl ReadableLoadOrderBase for TestLoadOrder {
282
        fn game_settings_base(&self) -> &GameSettings {
2,577✔
283
            &self.game_settings
2,577✔
284
        }
2,577✔
285

286
        fn plugins(&self) -> &[Plugin] {
2,654✔
287
            &self.plugins
2,654✔
288
        }
2,654✔
289
    }
290

291
    impl MutableLoadOrder for TestLoadOrder {
292
        fn plugins_mut(&mut self) -> &mut Vec<Plugin> {
2,572✔
293
            &mut self.plugins
2,572✔
294
        }
2,572✔
295

296
        fn insert_position(&self, plugin: &Plugin) -> Option<usize> {
1,283✔
297
            generic_insert_position(self.plugins(), plugin)
1,283✔
298
        }
1,283✔
299
    }
300

301
    fn prepare(game_id: GameId, game_dir: &Path) -> TestLoadOrder {
31✔
302
        let (game_settings, plugins) = mock_game_files(game_id, game_dir);
31✔
303
        TestLoadOrder {
31✔
304
            game_settings,
31✔
305
            plugins,
31✔
306
        }
31✔
307
    }
31✔
308

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

1✔
314
        assert!(add(&mut load_order, "Blank.esm").is_ok());
1✔
315
        assert!(add(&mut load_order, "Blank.esm").is_err());
1✔
316
    }
1✔
317

318
    #[test]
319
    fn add_should_error_if_given_a_master_that_would_hoist_a_non_master() {
1✔
320
        let tmp_dir = tempdir().unwrap();
1✔
321
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
322

1✔
323
        let plugins_dir = &load_order.game_settings().plugins_directory();
1✔
324
        copy_to_test_dir(
1✔
325
            "Blank - Different.esm",
1✔
326
            "Blank - Different.esm",
1✔
327
            load_order.game_settings(),
1✔
328
        );
1✔
329
        set_master_flag(&plugins_dir.join("Blank - Different.esm"), false).unwrap();
1✔
330
        assert!(add(&mut load_order, "Blank - Different.esm").is_ok());
1✔
331

332
        copy_to_test_dir(
1✔
333
            "Blank - Different Master Dependent.esm",
1✔
334
            "Blank - Different Master Dependent.esm",
1✔
335
            load_order.game_settings(),
1✔
336
        );
1✔
337

1✔
338
        assert!(add(&mut load_order, "Blank - Different Master Dependent.esm").is_err());
1✔
339
    }
1✔
340

341
    #[test]
342
    fn add_should_error_if_the_plugin_is_not_valid() {
1✔
343
        let tmp_dir = tempdir().unwrap();
1✔
344
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
345

1✔
346
        assert!(add(&mut load_order, "invalid.esm").is_err());
1✔
347
    }
1✔
348

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

1✔
354
        assert_eq!(1, add(&mut load_order, "Blank.esm").unwrap());
1✔
355
        assert_eq!(1, load_order.index_of("Blank.esm").unwrap());
1✔
356
    }
1✔
357

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

1✔
363
        assert_eq!(
1✔
364
            3,
1✔
365
            add(&mut load_order, "Blank - Master Dependent.esp").unwrap()
1✔
366
        );
1✔
367
        assert_eq!(
1✔
368
            3,
1✔
369
            load_order.index_of("Blank - Master Dependent.esp").unwrap()
1✔
370
        );
1✔
371
    }
1✔
372

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

1✔
378
        let plugins_dir = &load_order.game_settings().plugins_directory();
1✔
379
        copy_to_test_dir(
1✔
380
            "Blank - Different Master Dependent.esm",
1✔
381
            "Blank - Different Master Dependent.esm",
1✔
382
            load_order.game_settings(),
1✔
383
        );
1✔
384
        assert!(add(&mut load_order, "Blank - Different Master Dependent.esm").is_ok());
1✔
385

386
        copy_to_test_dir(
1✔
387
            "Blank - Different.esm",
1✔
388
            "Blank - Different.esm",
1✔
389
            load_order.game_settings(),
1✔
390
        );
1✔
391
        set_master_flag(&plugins_dir.join("Blank - Different.esm"), false).unwrap();
1✔
392
        assert_eq!(1, add(&mut load_order, "Blank - Different.esm").unwrap());
1✔
393
    }
1✔
394

395
    #[test]
396
    fn remove_should_error_if_the_plugin_is_not_in_the_load_order() {
1✔
397
        let tmp_dir = tempdir().unwrap();
1✔
398
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
399
        assert!(remove(&mut load_order, "Blank.esm").is_err());
1✔
400
    }
1✔
401

402
    #[test]
403
    fn remove_should_error_if_the_plugin_is_installed() {
1✔
404
        let tmp_dir = tempdir().unwrap();
1✔
405
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
406
        assert!(remove(&mut load_order, "Blank.esp").is_err());
1✔
407
    }
1✔
408

409
    #[test]
410
    fn remove_should_error_if_removing_a_master_would_leave_a_non_master_it_hoisted_loading_too_early(
1✔
411
    ) {
1✔
412
        let tmp_dir = tempdir().unwrap();
1✔
413
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
414

1✔
415
        let plugin_to_remove = "Blank - Different Master Dependent.esm";
1✔
416

1✔
417
        let plugins_dir = &load_order.game_settings().plugins_directory();
1✔
418
        copy_to_test_dir(
1✔
419
            plugin_to_remove,
1✔
420
            plugin_to_remove,
1✔
421
            load_order.game_settings(),
1✔
422
        );
1✔
423
        assert!(add(&mut load_order, plugin_to_remove).is_ok());
1✔
424

425
        copy_to_test_dir(
1✔
426
            "Blank - Different.esm",
1✔
427
            "Blank - Different.esm",
1✔
428
            load_order.game_settings(),
1✔
429
        );
1✔
430
        set_master_flag(&plugins_dir.join("Blank - Different.esm"), false).unwrap();
1✔
431
        assert_eq!(1, add(&mut load_order, "Blank - Different.esm").unwrap());
1✔
432

433
        copy_to_test_dir(
1✔
434
            "Blank - Master Dependent.esm",
1✔
435
            "Blank - Master Dependent.esm",
1✔
436
            load_order.game_settings(),
1✔
437
        );
1✔
438
        assert!(add(&mut load_order, "Blank - Master Dependent.esm").is_ok());
1✔
439

440
        let blank_master_dependent = load_order.plugins.remove(1);
1✔
441
        load_order.plugins.insert(3, blank_master_dependent);
1✔
442

1✔
443
        std::fs::remove_file(&plugins_dir.join(plugin_to_remove)).unwrap();
1✔
444

1✔
445
        match remove(&mut load_order, plugin_to_remove).unwrap_err() {
1✔
446
            Error::NonMasterBeforeMaster { master, non_master } => {
1✔
447
                assert_eq!("Blank - Different Master Dependent.esm", master);
1✔
448
                assert_eq!("Blank - Different.esm", non_master);
1✔
449
            }
UNCOV
450
            e => panic!("Unexpected error type: {:?}", e),
×
451
        }
452
    }
1✔
453

454
    #[test]
455
    fn remove_should_remove_the_given_plugin_from_the_load_order() {
1✔
456
        let tmp_dir = tempdir().unwrap();
1✔
457
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
458

1✔
459
        remove_file(
1✔
460
            load_order
1✔
461
                .game_settings()
1✔
462
                .plugins_directory()
1✔
463
                .join("Blank.esp"),
1✔
464
        )
1✔
465
        .unwrap();
1✔
466

1✔
467
        assert!(remove(&mut load_order, "Blank.esp").is_ok());
1✔
468
        assert!(load_order.index_of("Blank.esp").is_none());
1✔
469
    }
1✔
470

471
    #[test]
472
    fn activate_should_activate_the_plugin_with_the_given_filename() {
1✔
473
        let tmp_dir = tempdir().unwrap();
1✔
474
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
475

1✔
476
        assert!(activate(&mut load_order, "Blank - Different.esp").is_ok());
1✔
477
        assert!(load_order.is_active("Blank - Different.esp"));
1✔
478
    }
1✔
479

480
    #[test]
481
    fn activate_should_error_if_the_plugin_is_not_valid() {
1✔
482
        let tmp_dir = tempdir().unwrap();
1✔
483
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
484

1✔
485
        assert!(activate(&mut load_order, "missing.esp").is_err());
1✔
486
        assert!(load_order.index_of("missing.esp").is_none());
1✔
487
    }
1✔
488

489
    #[test]
490
    fn activate_should_error_if_the_plugin_is_not_already_in_the_load_order() {
1✔
491
        let tmp_dir = tempdir().unwrap();
1✔
492
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
493

1✔
494
        assert!(activate(&mut load_order, "Blank.esm").is_err());
1✔
495
        assert!(!load_order.is_active("Blank.esm"));
1✔
496
    }
1✔
497

498
    #[test]
499
    fn activate_should_be_case_insensitive() {
1✔
500
        let tmp_dir = tempdir().unwrap();
1✔
501
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
502

1✔
503
        assert!(activate(&mut load_order, "Blank - different.esp").is_ok());
1✔
504
        assert!(load_order.is_active("Blank - Different.esp"));
1✔
505
    }
1✔
506

507
    #[test]
508
    fn activate_should_throw_if_increasing_the_number_of_active_plugins_past_the_limit() {
1✔
509
        let tmp_dir = tempdir().unwrap();
1✔
510
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
511

512
        for i in 0..(MAX_ACTIVE_NORMAL_PLUGINS - 1) {
254✔
513
            let plugin = format!("{}.esp", i);
254✔
514
            copy_to_test_dir("Blank.esp", &plugin, &load_order.game_settings());
254✔
515
            load_and_insert(&mut load_order, &plugin);
254✔
516
            activate(&mut load_order, &plugin).unwrap();
254✔
517
        }
254✔
518

519
        assert!(activate(&mut load_order, "Blank - Different.esp").is_err());
1✔
520
        assert!(!load_order.is_active("Blank - Different.esp"));
1✔
521
    }
1✔
522

523
    #[test]
524
    fn activate_should_succeed_if_at_the_active_plugins_limit_and_the_plugin_is_already_active() {
1✔
525
        let tmp_dir = tempdir().unwrap();
1✔
526
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
527

528
        for i in 0..(MAX_ACTIVE_NORMAL_PLUGINS - 1) {
254✔
529
            let plugin = format!("{}.esp", i);
254✔
530
            copy_to_test_dir("Blank.esp", &plugin, &load_order.game_settings());
254✔
531
            load_and_insert(&mut load_order, &plugin);
254✔
532
            activate(&mut load_order, &plugin).unwrap();
254✔
533
        }
254✔
534

535
        assert!(load_order.is_active("Blank.esp"));
1✔
536
        assert!(activate(&mut load_order, "Blank.esp").is_ok());
1✔
537
    }
1✔
538

539
    #[test]
540
    fn activate_should_succeed_if_at_the_active_plugins_limit_and_the_plugin_is_an_override_plugin()
1✔
541
    {
1✔
542
        let tmp_dir = tempdir().unwrap();
1✔
543
        let mut load_order = prepare(GameId::Starfield, &tmp_dir.path());
1✔
544

545
        for i in 0..(MAX_ACTIVE_NORMAL_PLUGINS - 1) {
254✔
546
            let plugin = format!("{}.esp", i);
254✔
547
            copy_to_test_dir("Blank.esp", &plugin, &load_order.game_settings());
254✔
548
            load_and_insert(&mut load_order, &plugin);
254✔
549
            activate(&mut load_order, &plugin).unwrap();
254✔
550
        }
254✔
551

552
        let plugin = "Blank - Override.esp";
1✔
553
        load_and_insert(&mut load_order, &plugin);
1✔
554

1✔
555
        assert!(!load_order.is_active(plugin));
1✔
556

557
        assert!(activate(&mut load_order, plugin).is_ok());
1✔
558
        assert!(load_order.is_active(plugin));
1✔
559
    }
1✔
560

561
    #[test]
562
    fn activate_should_not_count_active_override_plugins_towards_limit() {
1✔
563
        let tmp_dir = tempdir().unwrap();
1✔
564
        let mut load_order = prepare(GameId::Starfield, &tmp_dir.path());
1✔
565

566
        for i in 0..(MAX_ACTIVE_NORMAL_PLUGINS - 1) {
254✔
567
            let plugin = format!("{}.esp", i);
254✔
568
            copy_to_test_dir("Blank - Override.esp", &plugin, &load_order.game_settings());
254✔
569
            load_and_insert(&mut load_order, &plugin);
254✔
570
            activate(&mut load_order, &plugin).unwrap();
254✔
571
        }
254✔
572

573
        let plugin = "Blank - Override.esp";
1✔
574
        load_and_insert(&mut load_order, &plugin);
1✔
575

1✔
576
        assert!(!load_order.is_active(plugin));
1✔
577

578
        assert!(activate(&mut load_order, plugin).is_ok());
1✔
579
        assert!(load_order.is_active(plugin));
1✔
580
    }
1✔
581

582
    #[test]
583
    fn deactivate_should_deactivate_the_plugin_with_the_given_filename() {
1✔
584
        let tmp_dir = tempdir().unwrap();
1✔
585
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
586

1✔
587
        assert!(load_order.is_active("Blank.esp"));
1✔
588
        assert!(deactivate(&mut load_order, "Blank.esp").is_ok());
1✔
589
        assert!(!load_order.is_active("Blank.esp"));
1✔
590
    }
1✔
591

592
    #[test]
593
    fn deactivate_should_error_if_the_plugin_is_not_in_the_load_order() {
1✔
594
        let tmp_dir = tempdir().unwrap();
1✔
595
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
596

1✔
597
        assert!(deactivate(&mut load_order, "missing.esp").is_err());
1✔
598
        assert!(load_order.index_of("missing.esp").is_none());
1✔
599
    }
1✔
600

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

1✔
606
        assert!(activate(&mut load_order, "Skyrim.esm").is_ok());
1✔
607
        assert!(deactivate(&mut load_order, "Skyrim.esm").is_err());
1✔
608
        assert!(load_order.is_active("Skyrim.esm"));
1✔
609
    }
1✔
610

611
    #[test]
612
    fn deactivate_should_error_if_given_a_missing_implicitly_active_plugin() {
1✔
613
        let tmp_dir = tempdir().unwrap();
1✔
614
        let mut load_order = prepare(GameId::Skyrim, &tmp_dir.path());
1✔
615

1✔
616
        assert!(deactivate(&mut load_order, "Update.esm").is_err());
1✔
617
        assert!(load_order.index_of("Update.esm").is_none());
1✔
618
    }
1✔
619

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

1✔
625
        assert!(!load_order.is_active("Blank - Different.esp"));
1✔
626
        assert!(deactivate(&mut load_order, "Blank - Different.esp").is_ok());
1✔
627
        assert!(!load_order.is_active("Blank - Different.esp"));
1✔
628
    }
1✔
629

630
    #[test]
631
    fn set_active_plugins_should_error_if_given_more_plugins_than_the_max_limit() {
1✔
632
        let tmp_dir = tempdir().unwrap();
1✔
633
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
634

1✔
635
        let active_plugins = [""; 256];
1✔
636
        assert!(set_active_plugins(&mut load_order, &active_plugins).is_err());
1✔
637
        assert_eq!(1, load_order.active_plugin_names().len());
1✔
638
    }
1✔
639

640
    #[test]
641
    fn set_active_plugins_should_error_if_passed_an_invalid_plugin_name() {
1✔
642
        let tmp_dir = tempdir().unwrap();
1✔
643
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
644

1✔
645
        let active_plugins = ["missing.esp"];
1✔
646
        assert!(set_active_plugins(&mut load_order, &active_plugins).is_err());
1✔
647
        assert_eq!(1, load_order.active_plugin_names().len());
1✔
648
    }
1✔
649

650
    #[test]
651
    fn set_active_plugins_should_error_if_the_given_plugins_are_missing_implicitly_active_plugins()
1✔
652
    {
1✔
653
        let tmp_dir = tempdir().unwrap();
1✔
654
        let mut load_order = prepare(GameId::Skyrim, &tmp_dir.path());
1✔
655

1✔
656
        let active_plugins = ["Blank.esp"];
1✔
657
        assert!(set_active_plugins(&mut load_order, &active_plugins).is_err());
1✔
658
        assert_eq!(1, load_order.active_plugin_names().len());
1✔
659
    }
1✔
660

661
    #[test]
662
    fn set_active_plugins_should_error_if_a_missing_implicitly_active_plugin_is_given() {
1✔
663
        let tmp_dir = tempdir().unwrap();
1✔
664
        let mut load_order = prepare(GameId::Skyrim, &tmp_dir.path());
1✔
665

1✔
666
        let active_plugins = ["Skyrim.esm", "Update.esm"];
1✔
667
        assert!(set_active_plugins(&mut load_order, &active_plugins).is_err());
1✔
668
        assert_eq!(1, load_order.active_plugin_names().len());
1✔
669
    }
1✔
670

671
    #[test]
672
    fn set_active_plugins_should_error_if_given_plugins_not_in_the_load_order() {
1✔
673
        let tmp_dir = tempdir().unwrap();
1✔
674
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
675

1✔
676
        let active_plugins = ["Blank - Master Dependent.esp", "Blàñk.esp"];
1✔
677
        assert!(set_active_plugins(&mut load_order, &active_plugins).is_err());
1✔
678
        assert!(!load_order.is_active("Blank - Master Dependent.esp"));
1✔
679
        assert!(load_order
1✔
680
            .index_of("Blank - Master Dependent.esp")
1✔
681
            .is_none());
1✔
682
        assert!(!load_order.is_active("Blàñk.esp"));
1✔
683
        assert!(load_order.index_of("Blàñk.esp").is_none());
1✔
684
    }
1✔
685

686
    #[test]
687
    fn set_active_plugins_should_deactivate_all_plugins_not_given() {
1✔
688
        let tmp_dir = tempdir().unwrap();
1✔
689
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
690

1✔
691
        let active_plugins = ["Blank - Different.esp"];
1✔
692
        assert!(load_order.is_active("Blank.esp"));
1✔
693
        assert!(set_active_plugins(&mut load_order, &active_plugins).is_ok());
1✔
694
        assert!(!load_order.is_active("Blank.esp"));
1✔
695
    }
1✔
696

697
    #[test]
698
    fn set_active_plugins_should_activate_all_given_plugins() {
1✔
699
        let tmp_dir = tempdir().unwrap();
1✔
700
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
701

1✔
702
        let active_plugins = ["Blank - Different.esp"];
1✔
703
        assert!(!load_order.is_active("Blank - Different.esp"));
1✔
704
        assert!(set_active_plugins(&mut load_order, &active_plugins).is_ok());
1✔
705
        assert!(load_order.is_active("Blank - Different.esp"));
1✔
706
    }
1✔
707

708
    #[test]
709
    fn set_active_plugins_should_not_count_override_plugins_towards_limit() {
1✔
710
        let tmp_dir = tempdir().unwrap();
1✔
711
        let mut load_order = prepare(GameId::Starfield, &tmp_dir.path());
1✔
712

1✔
713
        let blank_override = "Blank - Override.esp";
1✔
714
        load_and_insert(&mut load_order, blank_override);
1✔
715

1✔
716
        let mut active_plugins = vec!["Starfield.esm".to_string(), blank_override.to_string()];
1✔
717

718
        for i in 0..(MAX_ACTIVE_NORMAL_PLUGINS - 1) {
254✔
719
            let plugin = format!("{}.esp", i);
254✔
720
            copy_to_test_dir("Blank.esp", &plugin, &load_order.game_settings());
254✔
721
            load_and_insert(&mut load_order, &plugin);
254✔
722

254✔
723
            active_plugins.push(plugin);
254✔
724
        }
254✔
725

726
        let active_plugins: Vec<&str> = active_plugins.iter().map(|s| s.as_str()).collect();
256✔
727

1✔
728
        assert!(set_active_plugins(&mut load_order, &active_plugins).is_ok());
1✔
729
        assert_eq!(256, load_order.active_plugin_names().len());
1✔
730
    }
1✔
731
}
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

© 2025 Coveralls, Inc