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

Ortham / libloadorder / 6430662269

06 Oct 2023 10:46AM UTC coverage: 91.101% (+0.1%) from 90.97%
6430662269

push

github

Ortham
Fix ambiguity false positives for asterisk-based games

All active plugins have a defined load order, even if they're not early
loaders or listed in plugins.txt, so ambiguity only really exists if
there are inactive plugins that aren't in plugins.txt.

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

7309 of 8023 relevant lines covered (91.1%)

65548.59 hits per line

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

97.41
/src/load_order/tests.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

20
use std::convert::TryFrom;
21
use std::fmt::Display;
22
use std::fs::{create_dir_all, File, OpenOptions};
23
use std::io::{self, Seek, Write};
24
use std::path::Path;
25

26
use filetime::{set_file_times, FileTime};
27

28
use crate::enums::GameId;
29
use crate::enums::LoadOrderMethod;
30
use crate::game_settings::GameSettings;
31
use crate::load_order::strict_encode;
32
use crate::plugin::Plugin;
33
use crate::tests::copy_to_test_dir;
34

35
use super::mutable::MutableLoadOrder;
36

37
pub fn write_load_order_file<T: AsRef<str> + Display>(
11✔
38
    game_settings: &GameSettings,
11✔
39
    filenames: &[T],
11✔
40
) {
11✔
41
    let mut file = File::create(&game_settings.load_order_file().unwrap()).unwrap();
11✔
42

43
    for filename in filenames {
54✔
44
        writeln!(file, "{}", filename).unwrap();
43✔
45
    }
43✔
46
}
11✔
47

48
pub fn write_active_plugins_file<T: AsRef<str>>(game_settings: &GameSettings, filenames: &[T]) {
37✔
49
    let mut file = File::create(&game_settings.active_plugins_file()).unwrap();
37✔
50

37✔
51
    if game_settings.id() == GameId::Morrowind {
37✔
52
        writeln!(file, "isrealmorrowindini=false").unwrap();
2✔
53
        writeln!(file, "[Game Files]").unwrap();
2✔
54
    }
35✔
55

56
    for filename in filenames {
15,909✔
57
        if game_settings.id() == GameId::Morrowind {
15,872✔
58
            write!(file, "GameFile0=").unwrap();
4✔
59
        } else if game_settings.load_order_method() == LoadOrderMethod::Asterisk {
15,868✔
60
            write!(file, "*").unwrap();
15,821✔
61
        }
15,821✔
62

63
        file.write_all(&strict_encode(filename.as_ref()).unwrap())
15,872✔
64
            .unwrap();
15,872✔
65
        writeln!(file, "").unwrap();
15,872✔
66
    }
67
}
37✔
68

69
pub fn set_timestamps<T: AsRef<str>>(plugins_directory: &Path, filenames: &[T]) {
4✔
70
    for (index, filename) in filenames.iter().enumerate() {
22✔
71
        set_file_times(
22✔
72
            &plugins_directory.join(filename.as_ref()),
22✔
73
            FileTime::zero(),
22✔
74
            FileTime::from_unix_time(i64::try_from(index).unwrap(), 0),
22✔
75
        )
22✔
76
        .unwrap();
22✔
77
    }
22✔
78
}
4✔
79

80
pub fn game_settings_for_test(game_id: GameId, game_path: &Path) -> GameSettings {
213✔
81
    let local_path = game_path.join("local");
213✔
82
    create_dir_all(&local_path).unwrap();
213✔
83
    let my_games_path = game_path.join("my games");
213✔
84
    create_dir_all(&my_games_path).unwrap();
213✔
85

213✔
86
    GameSettings::with_local_and_my_games_paths(game_id, game_path, &local_path, my_games_path)
213✔
87
        .unwrap()
213✔
88
}
213✔
89

90
pub fn set_timestamp_order(plugin_names: &[&str], parent_path: &Path) {
205✔
91
    let mut timestamp = 1321009871;
205✔
92
    for plugin_name in plugin_names {
1,435✔
93
        let path = parent_path.join(plugin_name);
1,230✔
94
        filetime::set_file_mtime(path, FileTime::from_unix_time(timestamp, 0)).unwrap();
1,230✔
95
        timestamp += 60;
1,230✔
96
    }
1,230✔
97
}
205✔
98

99
pub fn mock_game_files(game_id: GameId, game_dir: &Path) -> (GameSettings, Vec<Plugin>) {
205✔
100
    let mut settings = game_settings_for_test(game_id, game_dir);
205✔
101

205✔
102
    copy_to_test_dir("Blank.esm", settings.master_file(), &settings);
205✔
103
    copy_to_test_dir("Blank.esm", "Blank.esm", &settings);
205✔
104
    copy_to_test_dir("Blank.esp", "Blank.esp", &settings);
205✔
105
    copy_to_test_dir("Blank - Different.esp", "Blank - Different.esp", &settings);
205✔
106
    copy_to_test_dir(
205✔
107
        "Blank - Master Dependent.esp",
205✔
108
        "Blank - Master Dependent.esp",
205✔
109
        &settings,
205✔
110
    );
205✔
111
    copy_to_test_dir("Blank.esp", "Blàñk.esp", &settings);
205✔
112

205✔
113
    let plugin_names = [
205✔
114
        settings.master_file(),
205✔
115
        "Blank.esm",
205✔
116
        "Blank.esp",
205✔
117
        "Blank - Different.esp",
205✔
118
        "Blank - Master Dependent.esp",
205✔
119
        "Blàñk.esp",
205✔
120
    ];
205✔
121
    set_timestamp_order(&plugin_names, &settings.plugins_directory());
205✔
122

205✔
123
    // Refresh settings to account for newly-created plugin files.
205✔
124
    settings.refresh_implicitly_active_plugins().unwrap();
205✔
125

205✔
126
    let plugins = vec![
205✔
127
        Plugin::new(settings.master_file(), &settings).unwrap(),
205✔
128
        Plugin::with_active("Blank.esp", &settings, true).unwrap(),
205✔
129
        Plugin::new("Blank - Different.esp", &settings).unwrap(),
205✔
130
    ];
205✔
131

205✔
132
    (settings, plugins)
205✔
133
}
205✔
134

135
pub fn to_owned(strs: Vec<&str>) -> Vec<String> {
16✔
136
    strs.into_iter().map(String::from).collect()
16✔
137
}
16✔
138

139
/// Set the master flag to be present or not for the given plugin.
140
/// Only valid for plugins for games other than Morrowind.
141
pub fn set_master_flag(plugin: &Path, present: bool) -> io::Result<()> {
14✔
142
    let mut file = OpenOptions::new().write(true).open(plugin)?;
14✔
143
    file.seek(io::SeekFrom::Start(8))?;
14✔
144

145
    let value = if present { 1 } else { 0 };
14✔
146
    file.write(&[value])?;
14✔
147

148
    Ok(())
14✔
149
}
14✔
150

151
pub fn set_override_flag(plugin: &Path, present: bool) -> io::Result<()> {
4✔
152
    let mut file = OpenOptions::new().write(true).open(plugin)?;
4✔
153
    file.seek(io::SeekFrom::Start(9))?;
4✔
154

155
    let value = if present { 2 } else { 0 };
4✔
156
    file.write(&[value])?;
4✔
157

158
    Ok(())
4✔
159
}
4✔
160

161
pub fn load_and_insert<T: MutableLoadOrder>(load_order: &mut T, plugin_name: &str) {
1,275✔
162
    let plugin = Plugin::new(plugin_name, load_order.game_settings()).unwrap();
1,275✔
163

1,275✔
164
    match load_order.insert_position(&plugin) {
1,275✔
165
        Some(position) => {
×
166
            load_order.plugins_mut().insert(position, plugin);
×
167
        }
×
168
        None => {
1,275✔
169
            load_order.plugins_mut().push(plugin);
1,275✔
170
        }
1,275✔
171
    }
172
}
1,275✔
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