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

MitMaro / git-interactive-rebase-tool / 14549551287

19 Apr 2025 01:21PM UTC coverage: 97.258% (-0.03%) from 97.292%
14549551287

push

github

MitMaro
Move diff loading to a thread

272 of 289 new or added lines in 17 files covered. (94.12%)

1 existing line in 1 file now uncovered.

4823 of 4959 relevant lines covered (97.26%)

2.77 hits per line

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

88.89
/src/diff/thread.rs
1
mod action;
2
mod load_status;
3
mod state;
4
mod update_handler;
5

6
use std::sync::Arc;
7

8
use captur::capture;
9
use parking_lot::Mutex;
10

11
pub(crate) use self::{action::Action, load_status::LoadStatus, state::State, update_handler::UpdateHandlerFn};
12
use crate::{
13
        diff::CommitDiffLoader,
14
        runtime::{Installer, Threadable},
15
};
16

17
pub(crate) const THREAD_NAME: &str = "diff";
18

19
#[derive(Debug)]
20
pub(crate) struct Thread<UpdateHandler: UpdateHandlerFn> {
21
        state: State,
22
        commit_diff_loader: Arc<Mutex<CommitDiffLoader>>,
23
        update_handler: Arc<UpdateHandler>,
24
}
25

26
impl<UpdateHandler> Threadable for Thread<UpdateHandler>
27
where UpdateHandler: UpdateHandlerFn + 'static
28
{
29
        fn install(&self, installer: &Installer) {
4✔
30
                let state = self.state();
4✔
31

32
                installer.spawn(THREAD_NAME, |notifier| {
8✔
33
                        let update_handler = Arc::clone(&self.update_handler);
8✔
34
                        let commit_diff_loader = Arc::clone(&self.commit_diff_loader);
8✔
35
                        move || {
8✔
36
                                capture!(notifier, state);
4✔
37

38
                                notifier.wait();
8✔
39

40
                                loop {
3✔
41
                                        notifier.wait();
4✔
42
                                        if state.is_ended() {
4✔
NEW
43
                                                break;
×
44
                                        }
45

46
                                        if state.is_cancelled() {
6✔
47
                                                commit_diff_loader.lock().reset();
2✔
48
                                        }
49

50
                                        let msg = state.receive_update();
6✔
51
                                        notifier.busy();
3✔
52
                                        match msg {
3✔
53
                                                Action::Load(hash) => {
1✔
54
                                                        state.resume();
1✔
55
                                                        let mut loader = commit_diff_loader.lock();
1✔
56
                                                        if let Err(e) = loader.load_diff(hash.as_str(), |s: LoadStatus| {
3✔
NEW
57
                                                                state.set_load_status(s);
×
NEW
58
                                                                update_handler();
×
NEW
59
                                                                state.is_cancelled()
×
60
                                                        }) {
61
                                                                state.set_load_status(LoadStatus::Error {
2✔
62
                                                                        msg: e.to_string(),
1✔
63
                                                                        code: e.code(),
1✔
64
                                                                });
65
                                                                state.cancel();
1✔
66
                                                                update_handler();
1✔
67
                                                        }
68
                                                },
NEW
69
                                                Action::StatusChange => {},
×
70
                                        }
71
                                }
72

73
                                notifier.request_end();
4✔
74
                                notifier.end();
4✔
75
                        }
76
                });
77
        }
78

79
        fn pause(&self) {
1✔
80
                self.state.cancel();
1✔
81
        }
82

83
        fn resume(&self) {
1✔
84
                self.state.resume();
1✔
85
        }
86

87
        fn end(&self) {
1✔
88
                self.state.end();
1✔
89
        }
90
}
91

92
impl<UpdateHandler> Thread<UpdateHandler>
93
where UpdateHandler: UpdateHandlerFn
94
{
95
        pub(crate) fn new(commit_diff_loader: CommitDiffLoader, update_handler: UpdateHandler) -> Self {
4✔
96
                let commit_diff = commit_diff_loader.commit_diff();
8✔
97
                Self {
98
                        state: State::new(commit_diff),
4✔
99
                        commit_diff_loader: Arc::new(Mutex::new(commit_diff_loader)),
8✔
100
                        update_handler: Arc::new(update_handler),
4✔
101
                }
102
        }
103

104
        pub(crate) fn state(&self) -> State {
4✔
105
                self.state.clone()
4✔
106
        }
107
}
108

109
#[cfg(test)]
110
mod tests {
111
        use std::{thread::sleep, time::Duration};
112

113
        use super::*;
114
        use crate::{
115
                diff::CommitDiffLoaderOptions,
116
                runtime::Status,
117
                test_helpers::{testers, with_temp_repository},
118
        };
119

120
        #[test]
121
        fn set_pause_resume() {
122
                with_temp_repository(|repository| {
123
                        let diff_loader = CommitDiffLoader::new(repository, CommitDiffLoaderOptions::new());
124
                        let diff = diff_loader.commit_diff();
125
                        diff.write().update(vec![], 1, 2, 3);
126
                        let thread = Thread::new(diff_loader, || {});
127

128
                        let state = thread.state();
129
                        let tester = testers::Threadable::new();
130
                        tester.start_threadable(&thread, THREAD_NAME);
131
                        tester.wait_for_status(&Status::Waiting);
132
                        thread.pause();
133
                        assert!(state.is_cancelled());
134
                        let mut pass = false;
135
                        for _ in 0..10 {
136
                                if diff.read().number_deletions() == 0 {
137
                                        pass = true;
138
                                        break;
139
                                }
140
                                sleep(Duration::from_millis(10));
141
                        }
142
                        assert!(pass);
143
                        thread.resume();
144
                        assert!(!state.is_cancelled());
145
                        state.end();
146
                        tester.wait_for_status(&Status::Ended);
147
                });
148
        }
149

150
        #[test]
151
        fn set_end() {
152
                with_temp_repository(|repository| {
153
                        let thread = Thread::new(CommitDiffLoader::new(repository, CommitDiffLoaderOptions::new()), || {});
154

155
                        let state = thread.state();
156
                        let tester = testers::Threadable::new();
157
                        tester.start_threadable(&thread, THREAD_NAME);
158
                        tester.wait_for_status(&Status::Waiting);
159
                        state.end();
160
                        assert!(state.is_ended());
161
                        tester.wait_for_status(&Status::Ended);
162
                });
163
        }
164

165
        #[test]
166
        fn diff_load_error() {
167
                with_temp_repository(|repository| {
168
                        let thread = Thread::new(CommitDiffLoader::new(repository, CommitDiffLoaderOptions::new()), || {});
169

170
                        let state = thread.state();
171
                        let tester = testers::Threadable::new();
172
                        tester.start_threadable(&thread, THREAD_NAME);
173
                        tester.wait_for_status(&Status::Waiting);
174

175
                        state.start_load("abc123");
176

177
                        let mut pass = false;
178
                        for _ in 0..10 {
179
                                if let LoadStatus::Error { .. } = state.load_status() {
180
                                        pass = true;
181
                                        break;
182
                                }
183
                                sleep(Duration::from_millis(10));
184
                        }
185
                        assert!(pass);
186
                        assert!(state.is_cancelled());
187

188
                        state.end();
189
                        tester.wait_for_status(&Status::Ended);
190
                });
191
        }
192
}
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