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

Ortham / libloadorder / 6525268815

15 Oct 2023 04:38PM UTC coverage: 91.193% (+0.02%) from 91.17%
6525268815

push

github

Ortham
Improve case-insensitivity

Lowercasing strings to isn't the correct way to perform case-insensitive
matching, so use unicase to do case folding.

28 of 28 new or added lines in 4 files covered. (100.0%)

7362 of 8073 relevant lines covered (91.19%)

65142.37 hits per line

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

95.45
/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),
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()));
2✔
88
            }
1✔
89

1✔
90
            if load_order.plugins()[index].is_master_file() {
1✔
91
                let next_master_pos = &load_order
×
92
                    .plugins()
×
93
                    .iter()
×
94
                    .skip(index + 1)
×
95
                    .position(|p| p.is_master_file());
×
96

97
                if let Some(position) = next_master_pos {
×
98
                    let previous_master_pos = &load_order.plugins()[..index]
×
99
                        .iter()
×
100
                        .rposition(|p| p.is_master_file())
×
101
                        .unwrap_or(0);
×
102

103
                    let masters = load_order.plugins()[*position].masters()?;
×
104
                    let master_names: HashSet<_> =
×
105
                        masters.iter().map(|m| UniCase::new(m.as_str())).collect();
×
106

×
107
                    if load_order
×
108
                        .plugins()
×
109
                        .iter()
×
110
                        .take(*position)
×
111
                        .skip(*previous_master_pos)
×
112
                        .any(|p| !master_names.contains(&UniCase::new(p.name())))
×
113
                    {
114
                        return Err(Error::NonMasterBeforeMaster);
×
115
                    }
×
116
                }
×
117
            }
1✔
118

119
            load_order.plugins_mut().remove(index);
1✔
120

1✔
121
            Ok(())
1✔
122
        }
123
        None => Err(Error::PluginNotFound(plugin_name.to_string())),
1✔
124
    }
125
}
4✔
126

127
#[derive(Clone, Copy, Debug, Default)]
1,291✔
128
struct PluginCounts {
129
    light: usize,
130
    normal: usize,
131
}
132

133
fn count_active_plugins<T: ReadableLoadOrderBase>(load_order: &T) -> PluginCounts {
1,285✔
134
    let mut counts = PluginCounts::default();
1,285✔
135

136
    for plugin in load_order.plugins().iter().filter(|p| p.is_active()) {
188,360✔
137
        if plugin.is_light_plugin() {
180,862✔
138
            counts.light += 1;
16,383✔
139
        } else if !plugin.is_override_plugin() {
164,479✔
140
            counts.normal += 1;
164,477✔
141
        }
164,477✔
142
    }
143

144
    counts
1,285✔
145
}
1,285✔
146

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

150
    for index in existing_plugin_indexes {
8,966✔
151
        let plugin = &existing_plugins[*index];
8,960✔
152

8,960✔
153
        if plugin.is_light_plugin() {
8,960✔
154
            counts.light += 1;
8,191✔
155
        } else if !plugin.is_override_plugin() {
8,191✔
156
            counts.normal += 1;
767✔
157
        }
767✔
158
    }
159

160
    counts
6✔
161
}
6✔
162

163
pub fn activate<T: MutableLoadOrder>(load_order: &mut T, plugin_name: &str) -> Result<(), Error> {
1,285✔
164
    let counts = count_active_plugins(load_order);
1,285✔
165

166
    let plugin = match load_order
1,285✔
167
        .plugins_mut()
1,285✔
168
        .iter_mut()
1,285✔
169
        .find(|p| p.name_matches(plugin_name))
175,758✔
170
    {
171
        Some(p) => p,
1,283✔
172
        None => return Err(Error::PluginNotFound(plugin_name.to_string())),
2✔
173
    };
174

175
    if !plugin.is_active() {
1,283✔
176
        let is_light = plugin.is_light_plugin();
1,282✔
177

1,282✔
178
        if (is_light && counts.light == MAX_ACTIVE_LIGHT_PLUGINS)
1,282✔
179
            || (!is_light
1,281✔
180
                && !plugin.is_override_plugin()
1,280✔
181
                && counts.normal == MAX_ACTIVE_NORMAL_PLUGINS)
1,276✔
182
        {
183
            return Err(Error::TooManyActivePlugins);
3✔
184
        } else {
185
            plugin.activate()?;
1,279✔
186
        }
187
    }
1✔
188

189
    Ok(())
1,280✔
190
}
1,285✔
191

192
pub fn deactivate<T: MutableLoadOrder>(load_order: &mut T, plugin_name: &str) -> Result<(), Error> {
5✔
193
    if load_order.game_settings().is_implicitly_active(plugin_name) {
5✔
194
        return Err(Error::ImplicitlyActivePlugin(plugin_name.to_string()));
2✔
195
    }
3✔
196

3✔
197
    load_order
3✔
198
        .plugins_mut()
3✔
199
        .iter_mut()
3✔
200
        .find(|p| p.name_matches(plugin_name))
8✔
201
        .ok_or_else(|| Error::PluginNotFound(plugin_name.to_string()))
3✔
202
        .map(|p| p.deactivate())
3✔
203
}
5✔
204

205
pub fn set_active_plugins<T: MutableLoadOrder>(
11✔
206
    load_order: &mut T,
11✔
207
    active_plugin_names: &[&str],
11✔
208
) -> Result<(), Error> {
11✔
209
    let existing_plugin_indices = load_order.lookup_plugins(active_plugin_names)?;
11✔
210

211
    let counts = count_plugins(load_order.plugins(), &existing_plugin_indices);
6✔
212

6✔
213
    if counts.normal > MAX_ACTIVE_NORMAL_PLUGINS || counts.light > MAX_ACTIVE_LIGHT_PLUGINS {
6✔
214
        return Err(Error::TooManyActivePlugins);
×
215
    }
6✔
216

217
    for plugin_name in load_order.game_settings().implicitly_active_plugins() {
15✔
218
        // If the plugin isn't installed, don't check that it's in the active
219
        // plugins list. Installed plugins will have already been loaded.
220
        if load_order.index_of(plugin_name).is_some()
15✔
221
            && !active_plugin_names.iter().any(|p| eq(*p, plugin_name))
4✔
222
        {
223
            return Err(Error::ImplicitlyActivePlugin(plugin_name.to_string()));
1✔
224
        }
14✔
225
    }
226

227
    load_order.deactivate_all();
5✔
228

229
    for index in existing_plugin_indices {
8,964✔
230
        load_order.plugins_mut()[index].activate()?;
8,959✔
231
    }
232

233
    Ok(())
5✔
234
}
11✔
235

236
pub fn create_parent_dirs(path: &Path) -> Result<(), Error> {
237
    if let Some(x) = path.parent() {
33✔
238
        if !x.exists() {
33✔
239
            create_dir_all(x)?
14✔
240
        }
19✔
241
    }
×
242
    Ok(())
33✔
243
}
33✔
244

245
#[cfg(test)]
246
mod tests {
247
    use super::*;
248

249
    use std::fs::remove_file;
250
    use std::path::Path;
251

252
    use tempfile::tempdir;
253

254
    use crate::enums::GameId;
255
    use crate::game_settings::GameSettings;
256
    use crate::load_order::mutable::{generic_insert_position, MutableLoadOrder};
257
    use crate::load_order::readable::{ReadableLoadOrder, ReadableLoadOrderBase};
258
    use crate::load_order::tests::{
259
        load_and_insert, mock_game_files, set_master_flag, set_override_flag,
260
    };
261
    use crate::tests::copy_to_test_dir;
262

263
    struct TestLoadOrder {
264
        game_settings: GameSettings,
265
        plugins: Vec<Plugin>,
266
    }
267

268
    impl ReadableLoadOrderBase for TestLoadOrder {
269
        fn game_settings_base(&self) -> &GameSettings {
2,584✔
270
            &self.game_settings
2,584✔
271
        }
2,584✔
272

273
        fn plugins(&self) -> &[Plugin] {
2,901✔
274
            &self.plugins
2,901✔
275
        }
2,901✔
276
    }
277

278
    impl MutableLoadOrder for TestLoadOrder {
279
        fn plugins_mut(&mut self) -> &mut Vec<Plugin> {
2,829✔
280
            &mut self.plugins
2,829✔
281
        }
2,829✔
282

283
        fn insert_position(&self, plugin: &Plugin) -> Option<usize> {
1,283✔
284
            generic_insert_position(self.plugins(), plugin)
1,283✔
285
        }
1,283✔
286
    }
287

288
    fn prepare(game_id: GameId, game_dir: &Path) -> TestLoadOrder {
31✔
289
        let (game_settings, plugins) = mock_game_files(game_id, game_dir);
31✔
290
        TestLoadOrder {
31✔
291
            game_settings,
31✔
292
            plugins,
31✔
293
        }
31✔
294
    }
31✔
295

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

1✔
301
        assert!(add(&mut load_order, "Blank.esm").is_ok());
1✔
302
        assert!(add(&mut load_order, "Blank.esm").is_err());
1✔
303
    }
1✔
304

305
    #[test]
1✔
306
    fn add_should_error_if_given_a_master_that_would_hoist_a_non_master() {
1✔
307
        let tmp_dir = tempdir().unwrap();
1✔
308
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
309

1✔
310
        let plugins_dir = &load_order.game_settings().plugins_directory();
1✔
311
        copy_to_test_dir(
1✔
312
            "Blank - Different.esm",
1✔
313
            "Blank - Different.esm",
1✔
314
            load_order.game_settings(),
1✔
315
        );
1✔
316
        set_master_flag(&plugins_dir.join("Blank - Different.esm"), false).unwrap();
1✔
317
        assert!(add(&mut load_order, "Blank - Different.esm").is_ok());
1✔
318

319
        copy_to_test_dir(
1✔
320
            "Blank - Different Master Dependent.esm",
1✔
321
            "Blank - Different Master Dependent.esm",
1✔
322
            load_order.game_settings(),
1✔
323
        );
1✔
324

1✔
325
        assert!(add(&mut load_order, "Blank - Different Master Dependent.esm").is_err());
1✔
326
    }
1✔
327

328
    #[test]
1✔
329
    fn add_should_error_if_the_plugin_is_not_valid() {
1✔
330
        let tmp_dir = tempdir().unwrap();
1✔
331
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
332

1✔
333
        assert!(add(&mut load_order, "invalid.esm").is_err());
1✔
334
    }
1✔
335

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

1✔
341
        assert_eq!(1, add(&mut load_order, "Blank.esm").unwrap());
1✔
342
        assert_eq!(1, load_order.index_of("Blank.esm").unwrap());
1✔
343
    }
1✔
344

345
    #[test]
1✔
346
    fn add_should_append_a_non_master() {
1✔
347
        let tmp_dir = tempdir().unwrap();
1✔
348
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
349

1✔
350
        assert_eq!(
1✔
351
            3,
1✔
352
            add(&mut load_order, "Blank - Master Dependent.esp").unwrap()
1✔
353
        );
1✔
354
        assert_eq!(
1✔
355
            3,
1✔
356
            load_order.index_of("Blank - Master Dependent.esp").unwrap()
1✔
357
        );
1✔
358
    }
1✔
359

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

1✔
365
        let plugins_dir = &load_order.game_settings().plugins_directory();
1✔
366
        copy_to_test_dir(
1✔
367
            "Blank - Different Master Dependent.esm",
1✔
368
            "Blank - Different Master Dependent.esm",
1✔
369
            load_order.game_settings(),
1✔
370
        );
1✔
371
        assert!(add(&mut load_order, "Blank - Different Master Dependent.esm").is_ok());
1✔
372

373
        copy_to_test_dir(
1✔
374
            "Blank - Different.esm",
1✔
375
            "Blank - Different.esm",
1✔
376
            load_order.game_settings(),
1✔
377
        );
1✔
378
        set_master_flag(&plugins_dir.join("Blank - Different.esm"), false).unwrap();
1✔
379
        assert_eq!(1, add(&mut load_order, "Blank - Different.esm").unwrap());
1✔
380
    }
1✔
381

382
    #[test]
1✔
383
    fn remove_should_error_if_the_plugin_is_not_in_the_load_order() {
1✔
384
        let tmp_dir = tempdir().unwrap();
1✔
385
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
386
        assert!(remove(&mut load_order, "Blank.esm").is_err());
1✔
387
    }
1✔
388

389
    #[test]
1✔
390
    fn remove_should_error_if_the_plugin_is_installed() {
1✔
391
        let tmp_dir = tempdir().unwrap();
1✔
392
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
393
        assert!(remove(&mut load_order, "Blank.esp").is_err());
1✔
394
    }
1✔
395

396
    #[test]
1✔
397
    fn remove_should_error_if_removing_a_master_would_leave_a_non_master_it_hoisted_loading_too_early(
1✔
398
    ) {
1✔
399
        let tmp_dir = tempdir().unwrap();
1✔
400
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
401

1✔
402
        let plugins_dir = &load_order.game_settings().plugins_directory();
1✔
403
        copy_to_test_dir(
1✔
404
            "Blank - Different Master Dependent.esm",
1✔
405
            "Blank - Different Master Dependent.esm",
1✔
406
            load_order.game_settings(),
1✔
407
        );
1✔
408
        assert!(add(&mut load_order, "Blank - Different Master Dependent.esm").is_ok());
1✔
409

410
        copy_to_test_dir(
1✔
411
            "Blank - Different.esm",
1✔
412
            "Blank - Different.esm",
1✔
413
            load_order.game_settings(),
1✔
414
        );
1✔
415
        set_master_flag(&plugins_dir.join("Blank - Different.esm"), false).unwrap();
1✔
416
        assert_eq!(1, add(&mut load_order, "Blank - Different.esm").unwrap());
1✔
417

418
        copy_to_test_dir(
1✔
419
            "Blank - Master Dependent.esm",
1✔
420
            "Blank - Master Dependent.esm",
1✔
421
            load_order.game_settings(),
1✔
422
        );
1✔
423
        assert!(add(&mut load_order, "Blank - Master Dependent.esm").is_ok());
1✔
424

425
        assert!(remove(&mut load_order, "Blank - Different Master Dependent.esm").is_err());
1✔
426
    }
1✔
427

428
    #[test]
1✔
429
    fn remove_should_remove_the_given_plugin_from_the_load_order() {
1✔
430
        let tmp_dir = tempdir().unwrap();
1✔
431
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
432

1✔
433
        remove_file(
1✔
434
            load_order
1✔
435
                .game_settings()
1✔
436
                .plugins_directory()
1✔
437
                .join("Blank.esp"),
1✔
438
        )
1✔
439
        .unwrap();
1✔
440

1✔
441
        assert!(remove(&mut load_order, "Blank.esp").is_ok());
1✔
442
        assert!(load_order.index_of("Blank.esp").is_none());
1✔
443
    }
1✔
444

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

1✔
450
        assert!(activate(&mut load_order, "Blank - Different.esp").is_ok());
1✔
451
        assert!(load_order.is_active("Blank - Different.esp"));
1✔
452
    }
1✔
453

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

1✔
459
        assert!(activate(&mut load_order, "missing.esp").is_err());
1✔
460
        assert!(load_order.index_of("missing.esp").is_none());
1✔
461
    }
1✔
462

463
    #[test]
1✔
464
    fn activate_should_error_if_the_plugin_is_not_already_in_the_load_order() {
1✔
465
        let tmp_dir = tempdir().unwrap();
1✔
466
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
467

1✔
468
        assert!(activate(&mut load_order, "Blank.esm").is_err());
1✔
469
        assert!(!load_order.is_active("Blank.esm"));
1✔
470
    }
1✔
471

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

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

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

486
        for i in 0..(MAX_ACTIVE_NORMAL_PLUGINS - 1) {
254✔
487
            let plugin = format!("{}.esp", i);
254✔
488
            copy_to_test_dir("Blank.esp", &plugin, &load_order.game_settings());
254✔
489
            load_and_insert(&mut load_order, &plugin);
254✔
490
            activate(&mut load_order, &plugin).unwrap();
254✔
491
        }
254✔
492

493
        assert!(activate(&mut load_order, "Blank - Different.esp").is_err());
1✔
494
        assert!(!load_order.is_active("Blank - Different.esp"));
1✔
495
    }
1✔
496

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

502
        for i in 0..(MAX_ACTIVE_NORMAL_PLUGINS - 1) {
254✔
503
            let plugin = format!("{}.esp", i);
254✔
504
            copy_to_test_dir("Blank.esp", &plugin, &load_order.game_settings());
254✔
505
            load_and_insert(&mut load_order, &plugin);
254✔
506
            activate(&mut load_order, &plugin).unwrap();
254✔
507
        }
254✔
508

509
        assert!(load_order.is_active("Blank.esp"));
1✔
510
        assert!(activate(&mut load_order, "Blank.esp").is_ok());
1✔
511
    }
1✔
512

513
    #[test]
1✔
514
    fn activate_should_succeed_if_at_the_active_plugins_limit_and_the_plugin_is_an_override_plugin()
1✔
515
    {
1✔
516
        let tmp_dir = tempdir().unwrap();
1✔
517
        let mut load_order = prepare(GameId::Starfield, &tmp_dir.path());
1✔
518

519
        for i in 0..(MAX_ACTIVE_NORMAL_PLUGINS - 1) {
254✔
520
            let plugin = format!("{}.esp", i);
254✔
521
            copy_to_test_dir("Blank.esp", &plugin, &load_order.game_settings());
254✔
522
            load_and_insert(&mut load_order, &plugin);
254✔
523
            activate(&mut load_order, &plugin).unwrap();
254✔
524
        }
254✔
525

526
        let plugin = "override.esp";
1✔
527
        copy_to_test_dir(
1✔
528
            "Blank - Different Plugin Dependent.esp",
1✔
529
            &plugin,
1✔
530
            &load_order.game_settings(),
1✔
531
        );
1✔
532
        set_override_flag(
1✔
533
            &load_order.game_settings().plugins_directory().join(plugin),
1✔
534
            true,
1✔
535
        )
1✔
536
        .unwrap();
1✔
537
        load_and_insert(&mut load_order, &plugin);
1✔
538

1✔
539
        assert!(!load_order.is_active(plugin));
1✔
540

541
        assert!(activate(&mut load_order, plugin).is_ok());
1✔
542
        assert!(load_order.is_active(plugin));
1✔
543
    }
1✔
544

545
    #[test]
1✔
546
    fn activate_should_not_count_active_override_plugins_towards_limit() {
1✔
547
        let tmp_dir = tempdir().unwrap();
1✔
548
        let mut load_order = prepare(GameId::Starfield, &tmp_dir.path());
1✔
549

550
        for i in 0..(MAX_ACTIVE_NORMAL_PLUGINS - 2) {
253✔
551
            let plugin = format!("{}.esp", i);
253✔
552
            copy_to_test_dir("Blank.esp", &plugin, &load_order.game_settings());
253✔
553
            load_and_insert(&mut load_order, &plugin);
253✔
554
            activate(&mut load_order, &plugin).unwrap();
253✔
555
        }
253✔
556

557
        // Activate an override plugin as the 255th active plugin.
558
        let plugin = "override.esp";
1✔
559
        copy_to_test_dir(
1✔
560
            "Blank - Different Plugin Dependent.esp",
1✔
561
            &plugin,
1✔
562
            &load_order.game_settings(),
1✔
563
        );
1✔
564
        set_override_flag(
1✔
565
            &load_order.game_settings().plugins_directory().join(plugin),
1✔
566
            true,
1✔
567
        )
1✔
568
        .unwrap();
1✔
569
        load_and_insert(&mut load_order, &plugin);
1✔
570
        activate(&mut load_order, &plugin).unwrap();
1✔
571

1✔
572
        assert!(activate(&mut load_order, "Blank - Different.esp").is_ok());
1✔
573
        assert!(load_order.is_active("Blank - Different.esp"));
1✔
574
    }
1✔
575

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

1✔
581
        assert!(load_order.is_active("Blank.esp"));
1✔
582
        assert!(deactivate(&mut load_order, "Blank.esp").is_ok());
1✔
583
        assert!(!load_order.is_active("Blank.esp"));
1✔
584
    }
1✔
585

586
    #[test]
1✔
587
    fn deactivate_should_error_if_the_plugin_is_not_in_the_load_order() {
1✔
588
        let tmp_dir = tempdir().unwrap();
1✔
589
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
590

1✔
591
        assert!(deactivate(&mut load_order, "missing.esp").is_err());
1✔
592
        assert!(load_order.index_of("missing.esp").is_none());
1✔
593
    }
1✔
594

595
    #[test]
1✔
596
    fn deactivate_should_error_if_given_an_implicitly_active_plugin() {
1✔
597
        let tmp_dir = tempdir().unwrap();
1✔
598
        let mut load_order = prepare(GameId::Skyrim, &tmp_dir.path());
1✔
599

1✔
600
        assert!(activate(&mut load_order, "Skyrim.esm").is_ok());
1✔
601
        assert!(deactivate(&mut load_order, "Skyrim.esm").is_err());
1✔
602
        assert!(load_order.is_active("Skyrim.esm"));
1✔
603
    }
1✔
604

605
    #[test]
1✔
606
    fn deactivate_should_error_if_given_a_missing_implicitly_active_plugin() {
1✔
607
        let tmp_dir = tempdir().unwrap();
1✔
608
        let mut load_order = prepare(GameId::Skyrim, &tmp_dir.path());
1✔
609

1✔
610
        assert!(deactivate(&mut load_order, "Update.esm").is_err());
1✔
611
        assert!(load_order.index_of("Update.esm").is_none());
1✔
612
    }
1✔
613

614
    #[test]
1✔
615
    fn deactivate_should_do_nothing_if_the_plugin_is_inactive() {
1✔
616
        let tmp_dir = tempdir().unwrap();
1✔
617
        let mut load_order = prepare(GameId::Skyrim, &tmp_dir.path());
1✔
618

1✔
619
        assert!(!load_order.is_active("Blank - Different.esp"));
1✔
620
        assert!(deactivate(&mut load_order, "Blank - Different.esp").is_ok());
1✔
621
        assert!(!load_order.is_active("Blank - Different.esp"));
1✔
622
    }
1✔
623

624
    #[test]
1✔
625
    fn set_active_plugins_should_error_if_given_more_plugins_than_the_max_limit() {
1✔
626
        let tmp_dir = tempdir().unwrap();
1✔
627
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
628

1✔
629
        let active_plugins = [""; 256];
1✔
630
        assert!(set_active_plugins(&mut load_order, &active_plugins).is_err());
1✔
631
        assert_eq!(1, load_order.active_plugin_names().len());
1✔
632
    }
1✔
633

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

1✔
639
        let active_plugins = ["missing.esp"];
1✔
640
        assert!(set_active_plugins(&mut load_order, &active_plugins).is_err());
1✔
641
        assert_eq!(1, load_order.active_plugin_names().len());
1✔
642
    }
1✔
643

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

1✔
650
        let active_plugins = ["Blank.esp"];
1✔
651
        assert!(set_active_plugins(&mut load_order, &active_plugins).is_err());
1✔
652
        assert_eq!(1, load_order.active_plugin_names().len());
1✔
653
    }
1✔
654

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

1✔
660
        let active_plugins = ["Skyrim.esm", "Update.esm"];
1✔
661
        assert!(set_active_plugins(&mut load_order, &active_plugins).is_err());
1✔
662
        assert_eq!(1, load_order.active_plugin_names().len());
1✔
663
    }
1✔
664

665
    #[test]
1✔
666
    fn set_active_plugins_should_error_if_given_plugins_not_in_the_load_order() {
1✔
667
        let tmp_dir = tempdir().unwrap();
1✔
668
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
669

1✔
670
        let active_plugins = ["Blank - Master Dependent.esp", "Blàñk.esp"];
1✔
671
        assert!(set_active_plugins(&mut load_order, &active_plugins).is_err());
1✔
672
        assert!(!load_order.is_active("Blank - Master Dependent.esp"));
1✔
673
        assert!(load_order
1✔
674
            .index_of("Blank - Master Dependent.esp")
1✔
675
            .is_none());
1✔
676
        assert!(!load_order.is_active("Blàñk.esp"));
1✔
677
        assert!(load_order.index_of("Blàñk.esp").is_none());
1✔
678
    }
1✔
679

680
    #[test]
1✔
681
    fn set_active_plugins_should_deactivate_all_plugins_not_given() {
1✔
682
        let tmp_dir = tempdir().unwrap();
1✔
683
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
684

1✔
685
        let active_plugins = ["Blank - Different.esp"];
1✔
686
        assert!(load_order.is_active("Blank.esp"));
1✔
687
        assert!(set_active_plugins(&mut load_order, &active_plugins).is_ok());
1✔
688
        assert!(!load_order.is_active("Blank.esp"));
1✔
689
    }
1✔
690

691
    #[test]
1✔
692
    fn set_active_plugins_should_activate_all_given_plugins() {
1✔
693
        let tmp_dir = tempdir().unwrap();
1✔
694
        let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());
1✔
695

1✔
696
        let active_plugins = ["Blank - Different.esp"];
1✔
697
        assert!(!load_order.is_active("Blank - Different.esp"));
1✔
698
        assert!(set_active_plugins(&mut load_order, &active_plugins).is_ok());
1✔
699
        assert!(load_order.is_active("Blank - Different.esp"));
1✔
700
    }
1✔
701

702
    #[test]
1✔
703
    fn set_active_plugins_should_not_count_existing_active_override_plugins_towards_limit() {
1✔
704
        let tmp_dir = tempdir().unwrap();
1✔
705
        let mut load_order = prepare(GameId::Starfield, &tmp_dir.path());
1✔
706

1✔
707
        let mut active_plugins = vec!["Starfield.esm".to_string()];
1✔
708

709
        for i in 0..(MAX_ACTIVE_NORMAL_PLUGINS - 1) {
254✔
710
            let plugin = format!("{}.esp", i);
254✔
711
            copy_to_test_dir("Blank.esp", &plugin, &load_order.game_settings());
254✔
712
            load_and_insert(&mut load_order, &plugin);
254✔
713
            activate(&mut load_order, &plugin).unwrap();
254✔
714

254✔
715
            active_plugins.push(plugin);
254✔
716
        }
254✔
717

718
        // Also activate a couple of override plugins.
719
        for i in 0..2 {
3✔
720
            let plugin = format!("{}.override.esp", i);
2✔
721
            copy_to_test_dir(
2✔
722
                "Blank - Different Plugin Dependent.esp",
2✔
723
                &plugin,
2✔
724
                &load_order.game_settings(),
2✔
725
            );
2✔
726
            set_override_flag(
2✔
727
                &load_order.game_settings().plugins_directory().join(&plugin),
2✔
728
                true,
2✔
729
            )
2✔
730
            .unwrap();
2✔
731
            load_and_insert(&mut load_order, &plugin);
2✔
732
            activate(&mut load_order, &plugin).unwrap();
2✔
733

2✔
734
            active_plugins.push(plugin);
2✔
735
        }
2✔
736

737
        let active_plugins: Vec<&str> = active_plugins.iter().map(|s| s.as_str()).collect();
257✔
738

1✔
739
        assert!(set_active_plugins(&mut load_order, &active_plugins).is_ok());
1✔
740
        assert_eq!(257, load_order.active_plugin_names().len());
1✔
741
    }
1✔
742
}
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