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

pythonbrad / afrim / 8129965345

03 Mar 2024 11:58AM UTC coverage: 98.854% (+0.07%) from 98.788%
8129965345

push

github

web-flow
feat(preprocessor): implement the serde feature (#179)

Now, we are able to serialize/deserialize the command enum.

1466 of 1483 relevant lines covered (98.85%)

60.85 hits per line

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

99.33
/engine/preprocessor/src/lib.rs
1
#![deny(missing_docs)]
2
//! Preprocess keyboard events for an input method.
3
//!
4
//! Enables the generation of keyboard event responses from a keyboard input event in an input method
5
//! engine.
6
//! The `afrim-preprocessor` crate is built on the top of the [`afrim-memory`](afrim_memory) crate.
7
//!
8
//! # Example
9
//!
10
//! ```
11
//! use afrim_preprocessor::{utils, Command, Preprocessor};
12
//! use keyboard_types::{
13
//!     webdriver::{self, Event},
14
//!     Key::*,
15
//! };
16
//! use std::{collections::VecDeque, rc::Rc};
17
//!
18
//! // Prepares the memory.
19
//! let data = utils::load_data("cc ç");
20
//! let text_buffer = utils::build_map(data);
21
//! let memory = Rc::new(text_buffer);
22
//!
23
//! // Builds the preprocessor.
24
//! let mut preprocessor = Preprocessor::new(memory, 8);
25
//!
26
//! // Process an input.
27
//! let input = "cc";
28
//! webdriver::send_keys(input)
29
//!     .into_iter()
30
//!     .for_each(|event| {
31
//!         match event {
32
//!             // Triggers the generated keyboard input event.
33
//!             Event::Keyboard(event) => preprocessor.process(event),
34
//!             _ => unimplemented!(),
35
//!         };
36
//!     });
37
//!
38
//! // Now let's look at the generated commands.
39
//! // The expected results without `inhibit` feature.
40
//! #[cfg(not(feature = "inhibit"))]
41
//! let mut expecteds = VecDeque::from(vec![
42
//!     Command::Pause,
43
//!     Command::Delete,
44
//!     Command::Delete,
45
//!     Command::CommitText("ç".to_owned()),
46
//!     Command::Resume,
47
//! ]);
48
//!
49
//! // The expected results with `inhibit` feature.
50
//! #[cfg(feature = "inhibit")]
51
//! let mut expecteds = VecDeque::from(vec![
52
//!     Command::Pause,
53
//!     Command::Delete,
54
//!     Command::Resume,
55
//!     Command::Pause,
56
//!     Command::Delete,
57
//!     Command::CommitText("ç".to_owned()),
58
//!     Command::Resume,
59
//! ]);
60
//!
61
//! // Verification.
62
//! while let Some(command) = preprocessor.pop_queue() {
63
//!     assert_eq!(command, expecteds.pop_front().unwrap());
64
//! }
65
//! ```
66
//! **Note**: When dealing with non latin languages. The inhibit feature permit to remove as possible
67
//! the non wanted characters (generally, latin characters).
68

69
mod message;
70

71
pub use crate::message::Command;
72
pub use afrim_memory::utils;
73
use afrim_memory::{Cursor, Node};
74
pub use keyboard_types::{Key, KeyState, KeyboardEvent};
75
use std::{collections::VecDeque, rc::Rc};
76

77
/// The main structure of the preprocessor.
78
#[derive(Debug)]
79
pub struct Preprocessor {
80
    cursor: Cursor,
81
    queue: VecDeque<Command>,
82
}
83

84
impl Preprocessor {
85
    /// Initializes a new preprocessor.
86
    ///
87
    /// The preprocessor needs a memory to operate. You have two options to build this memory.
88
    /// - Use the [`afrim-memory`](afrim_memory) crate.
89
    /// - Use the [`utils`](crate::utils) module.
90
    /// It also needs you set the capacity of his cursor. We recommend to set a capacity equal
91
    /// or greater than N times the maximun sequence length that you want to handle.
92
    /// Where N is the number of sequences that you want track in the cursor.
93
    ///
94
    /// Note that the cursor is the internal memory of the `afrim_preprocessor`.
95
    ///
96
    /// # Example
97
    ///
98
    /// ```
99
    /// use afrim_preprocessor::{Preprocessor, utils};
100
    /// use std::rc::Rc;
101
    ///
102
    /// // We prepare the memory.
103
    /// let data = utils::load_data("uuaf3    ʉ̄ɑ̄");
104
    /// let text_buffer = utils::build_map(data);
105
    /// let memory = Rc::new(text_buffer);
106
    ///
107
    /// // We initialize our preprocessor.
108
    /// let preprocessor = Preprocessor::new(memory, 8);
109
    /// ```
110
    pub fn new(memory: Rc<Node>, buffer_size: usize) -> Self {
5✔
111
        let cursor = Cursor::new(memory, buffer_size);
5✔
112
        let queue = VecDeque::with_capacity(15);
5✔
113

5✔
114
        Self { cursor, queue }
5✔
115
    }
5✔
116

117
    // Cancel the previous operation.
118
    fn rollback(&mut self) -> bool {
44✔
119
        if let Some(out) = self.cursor.undo() {
44✔
120
            #[cfg(feature = "inhibit")]
121
            let start = 0;
122
            #[cfg(not(feature = "inhibit"))]
123
            let start = 1;
27✔
124
            let end = out.chars().count();
27✔
125

27✔
126
            (start..end).for_each(|_| self.queue.push_back(Command::Delete));
27✔
127

128
            // Clear the remaining code
129
            while let (None, 1.., ..) = self.cursor.state() {
53✔
130
                self.cursor.undo();
26✔
131
            }
26✔
132

133
            if let (Some(_in), ..) = self.cursor.state() {
27✔
134
                self.queue.push_back(Command::CommitText(_in));
11✔
135
            }
16✔
136

137
            true
27✔
138
        } else {
139
            false
17✔
140
        }
141
    }
44✔
142

143
    // Cancel the previous operation.
144
    //
145
    // Note that it handles the delete by itself.
146
    #[cfg(not(feature = "inhibit"))]
147
    fn hard_rollback(&mut self) -> bool {
14✔
148
        self.queue.push_back(Command::Delete);
14✔
149
        self.rollback()
14✔
150
    }
14✔
151

152
    // Cancel the previous opeartion.
153
    //
154
    // Note that the delete is supposed already executed.
155
    fn soft_rollback(&mut self) -> bool {
30✔
156
        self.queue.push_back(Command::CleanDelete);
30✔
157
        self.rollback()
30✔
158
    }
30✔
159

160
    /// Preprocess the keyboard input event and returns infos on his internal changes (change on
161
    /// the cursor and/or something to commit).
162
    ///
163
    /// It's useful when you process keyboard input events in bulk. Whether there is something that
164
    /// you want to do based on this information, you can decide how to continue.
165
    ///
166
    /// # Example
167
    ///
168
    /// ```
169
    /// use afrim_preprocessor::{Command, Preprocessor, utils};
170
    /// use keyboard_types::{Key::*, KeyboardEvent};
171
    /// use std::{collections::VecDeque, rc::Rc};
172
    ///
173
    /// // We prepare the memory.
174
    /// let data = utils::load_data("i3  ī");
175
    /// let text_buffer = utils::build_map(data);
176
    /// let memory = Rc::new(text_buffer);
177
    ///
178
    /// let mut preprocessor = Preprocessor::new(memory, 8);
179
    ///
180
    /// // We process the input.
181
    /// // let input = "si3";
182
    ///
183
    /// let info = preprocessor.process(KeyboardEvent {
184
    ///     key: Character("s".to_string()),
185
    ///     ..Default::default()
186
    /// });
187
    /// assert_eq!(info, (true, false));
188
    ///
189
    /// let info = preprocessor.process(KeyboardEvent {
190
    ///     key: Character("i".to_string()),
191
    ///     ..Default::default()
192
    /// });
193
    /// assert_eq!(info, (true, false));
194
    ///
195
    /// let info = preprocessor.process(KeyboardEvent {
196
    ///     key: Character("3".to_string()),
197
    ///     ..Default::default()
198
    /// });
199
    /// assert_eq!(info, (true, true));
200
    ///
201
    /// // The input inside the preprocessor.
202
    /// assert_eq!(preprocessor.get_input(), "si3".to_owned());
203
    ///
204
    /// // The generated commands.
205
    /// // The expected results without inhibit feature.
206
    /// #[cfg(not(feature = "inhibit"))]
207
    /// let mut expecteds = VecDeque::from(vec![
208
    ///     Command::Pause,
209
    ///     Command::Delete,
210
    ///     Command::Delete,
211
    ///     Command::CommitText("ī".to_owned()),
212
    ///     Command::Resume,
213
    /// ]);
214
    ///
215
    /// // The expected results with inhibit feature.
216
    /// #[cfg(feature = "inhibit")]
217
    /// let mut expecteds = VecDeque::from(vec![
218
    ///     Command::Pause,
219
    ///     Command::Delete,
220
    ///     Command::Resume,
221
    ///     Command::Pause,
222
    ///     Command::Delete,
223
    ///     Command::Resume,
224
    ///     Command::Pause,
225
    ///     Command::Delete,
226
    ///     Command::CommitText("ī".to_owned()),
227
    ///     Command::Resume,
228
    /// ]);
229
    ///
230
    /// // Verification.
231
    /// while let Some(command) = preprocessor.pop_queue() {
232
    ///     dbg!(command.clone());
233
    ///     assert_eq!(command, expecteds.pop_front().unwrap());
234
    /// }
235
    /// ```
236
    pub fn process(&mut self, event: KeyboardEvent) -> (bool, bool) {
286✔
237
        let (mut changed, mut committed) = (false, false);
286✔
238

286✔
239
        match (event.state, event.key) {
286✔
240
            (KeyState::Down, Key::Backspace) => {
30✔
241
                #[cfg(not(feature = "inhibit"))]
30✔
242
                {
30✔
243
                    self.pause();
30✔
244
                    committed = self.soft_rollback();
30✔
245
                    self.resume();
30✔
246
                }
30✔
247
                #[cfg(feature = "inhibit")]
30✔
248
                self.cursor.clear();
30✔
249
                changed = true;
30✔
250
            }
30✔
251
            (KeyState::Down, Key::Character(character))
85✔
252
                if character
85✔
253
                    .chars()
85✔
254
                    .next()
85✔
255
                    .map(|e| e.is_alphanumeric() || e.is_ascii_punctuation())
85✔
256
                    .unwrap_or(false) =>
85✔
257
            {
85✔
258
                #[cfg(feature = "inhibit")]
85✔
259
                self.pause();
85✔
260
                #[cfg(feature = "inhibit")]
85✔
261
                self.queue.push_back(Command::Delete);
85✔
262

85✔
263
                let character = character.chars().next().unwrap();
85✔
264

265
                if let Some(_in) = self.cursor.hit(character) {
85✔
266
                    #[cfg(not(feature = "inhibit"))]
267
                    self.pause();
35✔
268
                    let mut prev_cursor = self.cursor.clone();
35✔
269
                    prev_cursor.undo();
35✔
270
                    #[cfg(not(feature = "inhibit"))]
35✔
271
                    self.queue.push_back(Command::Delete);
35✔
272

273
                    // Remove the remaining code
274
                    while let (None, 1.., ..) = prev_cursor.state() {
68✔
275
                        prev_cursor.undo();
33✔
276
                        #[cfg(not(feature = "inhibit"))]
33✔
277
                        self.queue.push_back(Command::Delete);
33✔
278
                    }
33✔
279

280
                    if let (Some(out), ..) = prev_cursor.state() {
35✔
281
                        (0..out.chars().count()).for_each(|_| self.queue.push_back(Command::Delete))
16✔
282
                    }
21✔
283

284
                    self.queue.push_back(Command::CommitText(_in));
35✔
285
                    #[cfg(not(feature = "inhibit"))]
35✔
286
                    self.resume();
35✔
287
                    committed = true;
35✔
288
                };
50✔
289

290
                #[cfg(feature = "inhibit")]
291
                self.resume();
292
                changed = true;
85✔
293
            }
294
            (KeyState::Down, Key::Shift | Key::CapsLock) => (),
5✔
295
            (KeyState::Down, _) => {
7✔
296
                self.cursor.clear();
7✔
297
                changed = true;
7✔
298
            }
7✔
299
            _ => (),
159✔
300
        };
301

302
        (changed, committed)
286✔
303
    }
286✔
304

305
    /// Commit a text.
306
    ///
307
    /// Generate a command to ensure the commitment of this text.
308
    /// Useful when you want deal with auto-completion.
309
    ///
310
    /// **Note**: Before any commitment, the preprocessor make sure to discard the current input.
311
    ///
312
    /// # Example
313
    ///
314
    /// ```
315
    /// use afrim_preprocessor::{Command, Preprocessor, utils};
316
    /// use keyboard_types::{Key::*, KeyboardEvent};
317
    /// use std::{collections::VecDeque, rc::Rc};
318
    ///
319
    /// // We prepare the memory.
320
    /// let data = utils::load_data("i3  ī");
321
    /// let text_buffer = utils::build_map(data);
322
    /// let memory = Rc::new(text_buffer);
323
    ///
324
    /// let mut preprocessor = Preprocessor::new(memory, 8);
325
    ///
326
    /// // We process the input.
327
    /// // let input = "si3";
328
    /// preprocessor.process(KeyboardEvent {
329
    ///     key: Character("s".to_string()),
330
    ///     ..Default::default()
331
    /// });
332
    ///
333
    /// preprocessor.commit("sī");
334
    ///
335
    /// // The generated commands.
336
    /// // The expected results without inhibit feature.
337
    /// #[cfg(not(feature = "inhibit"))]
338
    /// let mut expecteds = VecDeque::from(vec![
339
    ///     Command::Pause,
340
    ///     Command::Delete,
341
    ///     Command::CommitText("sī".to_owned()),
342
    ///     Command::Resume,
343
    /// ]);
344
    ///
345
    /// // The expected results with inhibit feature.
346
    /// #[cfg(feature = "inhibit")]
347
    /// let mut expecteds = VecDeque::from(vec![
348
    ///     Command::Pause,
349
    ///     Command::Delete,
350
    ///     Command::Resume,
351
    ///     Command::Pause,
352
    ///     Command::CleanDelete,
353
    ///     Command::CommitText("sī".to_owned()),
354
    ///     Command::Resume,
355
    /// ]);
356
    ///
357
    /// // Verification.
358
    /// while let Some(command) = preprocessor.pop_queue() {
359
    ///     assert_eq!(command, expecteds.pop_front().unwrap());
360
    /// }
361
    /// ```
362
    pub fn commit(&mut self, text: &str) {
5✔
363
        self.pause();
5✔
364

365
        while !self.cursor.is_empty() {
19✔
366
            #[cfg(not(feature = "inhibit"))]
14✔
367
            self.hard_rollback();
14✔
368
            #[cfg(feature = "inhibit")]
14✔
369
            self.soft_rollback();
14✔
370
        }
14✔
371
        #[cfg(feature = "inhibit")]
372
        self.cursor.clear();
373
        self.queue.push_back(Command::CommitText(text.to_owned()));
5✔
374
        self.resume();
5✔
375
        // We clear the buffer
5✔
376
        self.cursor.clear();
5✔
377
    }
5✔
378

379
    // Pauses the keyboard event listerner.
380
    fn pause(&mut self) {
70✔
381
        self.queue.push_back(Command::Pause);
70✔
382
    }
70✔
383

384
    // Resumes the keyboard event listener.
385
    fn resume(&mut self) {
70✔
386
        self.queue.push_back(Command::Resume);
70✔
387
    }
70✔
388

389
    /// Returns the input present in the internal memory.
390
    ///
391
    /// It's always useful to know what is inside the memory of the preprocessor for debugging.
392
    /// **Note**: The input inside the preprocessor is not always the same than the original because
393
    /// of the limited capacity of his internal cursor.
394
    ///
395
    /// # Example
396
    ///
397
    /// ```
398
    /// use afrim_preprocessor::{Command, Preprocessor, utils};
399
    /// use keyboard_types::{Key::*, webdriver::{self, Event}};
400
    /// use std::{collections::VecDeque, rc::Rc};
401
    ///
402
    /// // We prepare the memory.
403
    /// let data = utils::load_data("i3  ī");
404
    /// let text_buffer = utils::build_map(data);
405
    /// let memory = Rc::new(text_buffer);
406
    ///
407
    /// let mut preprocessor = Preprocessor::new(memory, 4);
408
    ///
409
    /// // We process the input.
410
    /// let input = "si3";
411
    /// webdriver::send_keys(input)
412
    ///     .into_iter()
413
    ///     .for_each(|event| {
414
    ///         match event {
415
    ///             // Triggers the generated keyboard input event.
416
    ///             Event::Keyboard(event) => preprocessor.process(event),
417
    ///             _ => unimplemented!(),
418
    ///         };
419
    ///     });
420
    ///
421
    /// // The input inside the processor.
422
    /// assert_eq!(preprocessor.get_input(), "si3".to_owned());
423
    pub fn get_input(&self) -> String {
73✔
424
        self.cursor
73✔
425
            .to_sequence()
73✔
426
            .into_iter()
73✔
427
            .filter(|c| *c != '\0')
746✔
428
            .collect::<String>()
73✔
429
    }
73✔
430

431
    /// Returns the next command to be executed.
432
    ///
433
    /// The next command is dropped from the queue and can't be returned anymore.
434
    ///
435
    /// # Example
436
    ///
437
    /// ```
438
    /// use afrim_preprocessor::{Command, Preprocessor, utils};
439
    /// use std::{collections::VecDeque, rc::Rc};
440
    ///
441
    /// // We prepare the memory.
442
    /// let text_buffer = utils::build_map(vec![]);
443
    /// let memory = Rc::new(text_buffer);
444
    ///
445
    /// let mut preprocessor = Preprocessor::new(memory, 8);
446
    /// preprocessor.commit("hello");
447
    ///
448
    /// // The expected results.
449
    /// let mut expecteds = VecDeque::from(vec![
450
    ///     Command::Pause,
451
    ///     Command::CommitText("hello".to_owned()),
452
    ///     Command::Resume,
453
    /// ]);
454
    ///
455
    /// // Verification.
456
    /// while let Some(command) = preprocessor.pop_queue() {
457
    ///     assert_eq!(command, expecteds.pop_front().unwrap());
458
    /// }
459
    pub fn pop_queue(&mut self) -> Option<Command> {
525✔
460
        self.queue.pop_front()
525✔
461
    }
525✔
462

463
    /// Clears the queue.
464
    ///
465
    /// # Example
466
    ///
467
    /// ```
468
    /// use afrim_preprocessor::{Preprocessor, utils};
469
    /// use std::rc::Rc;
470
    ///
471
    /// let data =
472
    /// utils::load_data("n* ŋ");
473
    /// let text_buffer = utils::build_map(data);
474
    /// let memory = Rc::new(text_buffer);
475
    ///
476
    /// let mut preprocessor = Preprocessor::new(memory, 8);
477
    /// preprocessor.commit("hi");
478
    /// preprocessor.clear_queue();
479
    ///
480
    /// assert_eq!(preprocessor.pop_queue(), None);
481
    /// ```
482
    pub fn clear_queue(&mut self) {
1✔
483
        self.queue.clear();
1✔
484
    }
1✔
485
}
486

487
#[cfg(test)]
488
mod tests {
489
    use crate::message::Command;
490
    use crate::utils;
491
    use crate::Preprocessor;
492
    use keyboard_types::{
493
        webdriver::{self, Event},
494
        Key::*,
495
    };
496
    use std::collections::VecDeque;
497

498
    #[test]
1✔
499
    fn test_process() {
1✔
500
        use std::rc::Rc;
1✔
501

1✔
502
        let data = utils::load_data("ccced ç\ncc ç");
1✔
503
        let memory = utils::build_map(data);
1✔
504
        let mut preprocessor = Preprocessor::new(Rc::new(memory), 8);
1✔
505
        webdriver::send_keys("ccced").into_iter().for_each(|e| {
10✔
506
            match e {
10✔
507
                Event::Keyboard(e) => preprocessor.process(e),
10✔
508
                _ => unimplemented!(),
×
509
            };
510
        });
10✔
511
        let mut expecteds = VecDeque::from(vec![
1✔
512
            // c c
1✔
513
            Command::Pause,
1✔
514
            Command::Delete,
1✔
515
            #[cfg(feature = "inhibit")]
1✔
516
            Command::Resume,
1✔
517
            #[cfg(feature = "inhibit")]
1✔
518
            Command::Pause,
1✔
519
            Command::Delete,
1✔
520
            Command::CommitText("ç".to_owned()),
1✔
521
            Command::Resume,
1✔
522
            // c e d
1✔
523
            Command::Pause,
1✔
524
            Command::Delete,
1✔
525
            #[cfg(feature = "inhibit")]
1✔
526
            Command::Resume,
1✔
527
            #[cfg(feature = "inhibit")]
1✔
528
            Command::Pause,
1✔
529
            Command::Delete,
1✔
530
            #[cfg(feature = "inhibit")]
1✔
531
            Command::Resume,
1✔
532
            #[cfg(feature = "inhibit")]
1✔
533
            Command::Pause,
1✔
534
            Command::Delete,
1✔
535
            Command::Delete,
1✔
536
            Command::CommitText("ç".to_owned()),
1✔
537
            Command::Resume,
1✔
538
        ]);
1✔
539

540
        while let Some(command) = preprocessor.pop_queue() {
13✔
541
            assert_eq!(command, expecteds.pop_front().unwrap());
12✔
542
        }
543
    }
1✔
544

545
    #[test]
1✔
546
    fn test_commit() {
1✔
547
        use afrim_memory::Node;
1✔
548
        use keyboard_types::KeyboardEvent;
1✔
549

1✔
550
        let mut preprocessor = Preprocessor::new(Node::default().into(), 8);
1✔
551
        preprocessor.process(KeyboardEvent {
1✔
552
            key: Character("a".to_owned()),
1✔
553
            ..Default::default()
1✔
554
        });
1✔
555
        preprocessor.commit("word");
1✔
556

1✔
557
        let mut expecteds = VecDeque::from(vec![
1✔
558
            Command::Pause,
1✔
559
            #[cfg(feature = "inhibit")]
1✔
560
            Command::Delete,
1✔
561
            #[cfg(feature = "inhibit")]
1✔
562
            Command::Resume,
1✔
563
            #[cfg(feature = "inhibit")]
1✔
564
            Command::Pause,
1✔
565
            #[cfg(feature = "inhibit")]
1✔
566
            Command::CleanDelete,
1✔
567
            #[cfg(not(feature = "inhibit"))]
1✔
568
            Command::Delete,
1✔
569
            Command::CommitText("word".to_owned()),
1✔
570
            Command::Resume,
1✔
571
        ]);
1✔
572

573
        while let Some(command) = preprocessor.pop_queue() {
5✔
574
            assert_eq!(command, expecteds.pop_front().unwrap());
4✔
575
        }
576
    }
1✔
577

578
    #[test]
1✔
579
    fn test_rollback() {
1✔
580
        use keyboard_types::KeyboardEvent;
1✔
581
        use std::rc::Rc;
1✔
582

1✔
583
        let data = utils::load_data("ccced ç\ncc ç");
1✔
584
        let memory = utils::build_map(data);
1✔
585
        let mut preprocessor = Preprocessor::new(Rc::new(memory), 8);
1✔
586
        let backspace_event = KeyboardEvent {
1✔
587
            key: Backspace,
1✔
588
            ..Default::default()
1✔
589
        };
1✔
590

1✔
591
        webdriver::send_keys("ccced").into_iter().for_each(|e| {
10✔
592
            match e {
10✔
593
                Event::Keyboard(e) => preprocessor.process(e),
10✔
594
                _ => unimplemented!(),
×
595
            };
596
        });
10✔
597

1✔
598
        preprocessor.clear_queue();
1✔
599
        assert_eq!(preprocessor.get_input(), "ccced".to_owned());
1✔
600
        preprocessor.process(backspace_event.clone());
1✔
601
        #[cfg(not(feature = "inhibit"))]
1✔
602
        assert_eq!(preprocessor.get_input(), "cc".to_owned());
1✔
603
        #[cfg(not(feature = "inhibit"))]
604
        preprocessor.process(backspace_event);
1✔
605
        assert_eq!(preprocessor.get_input(), "".to_owned());
1✔
606

607
        let mut expecteds = VecDeque::from(vec![
1✔
608
            Command::Pause,
1✔
609
            #[cfg(not(feature = "inhibit"))]
1✔
610
            Command::CleanDelete,
1✔
611
            Command::CommitText("ç".to_owned()),
1✔
612
            Command::Resume,
1✔
613
            #[cfg(not(feature = "inhibit"))]
1✔
614
            Command::Pause,
1✔
615
            #[cfg(not(feature = "inhibit"))]
1✔
616
            Command::CleanDelete,
1✔
617
            #[cfg(not(feature = "inhibit"))]
1✔
618
            Command::Resume,
1✔
619
        ]);
1✔
620

621
        while let Some(command) = preprocessor.pop_queue() {
8✔
622
            assert_eq!(command, expecteds.pop_front().unwrap());
7✔
623
        }
624
    }
1✔
625

626
    #[test]
1✔
627
    fn test_advanced() {
1✔
628
        use std::{fs, rc::Rc};
1✔
629

1✔
630
        let data = fs::read_to_string("./data/sample.txt").unwrap();
1✔
631
        let data = utils::load_data(&data);
1✔
632
        let memory = utils::build_map(data);
1✔
633
        let mut preprocessor = Preprocessor::new(Rc::new(memory), 64);
1✔
634

1✔
635
        webdriver::send_keys(
1✔
636
            "u\u{E003}uu\u{E003}uc_ceduuaf3afafaff3uu3\
1✔
637
            \u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}"
1✔
638
        ).into_iter().for_each(|e| {
80✔
639
            match e {
80✔
640
                Event::Keyboard(e) => preprocessor.process(e),
80✔
641
                _ => unimplemented!(),
×
642
            };
643
        });
80✔
644

1✔
645
        let mut expecteds = VecDeque::from(vec![
1✔
646
            // Process
1✔
647
            // u backspace
1✔
648
            Command::Pause,
1✔
649
            #[cfg(feature = "inhibit")]
1✔
650
            Command::Delete,
1✔
651
            #[cfg(feature = "inhibit")]
1✔
652
            Command::Resume,
1✔
653
            #[cfg(not(feature = "inhibit"))]
1✔
654
            Command::CleanDelete,
1✔
655
            #[cfg(not(feature = "inhibit"))]
1✔
656
            Command::Resume,
1✔
657
            // u u backspace
1✔
658
            Command::Pause,
1✔
659
            Command::Delete,
1✔
660
            #[cfg(feature = "inhibit")]
1✔
661
            Command::Resume,
1✔
662
            #[cfg(feature = "inhibit")]
1✔
663
            Command::Pause,
1✔
664
            Command::Delete,
1✔
665
            Command::CommitText("ʉ".to_owned()),
1✔
666
            Command::Resume,
1✔
667
            #[cfg(not(feature = "inhibit"))]
1✔
668
            Command::Pause,
1✔
669
            #[cfg(not(feature = "inhibit"))]
1✔
670
            Command::CleanDelete,
1✔
671
            #[cfg(not(feature = "inhibit"))]
1✔
672
            Command::Resume,
1✔
673
            // u
1✔
674
            #[cfg(feature = "inhibit")]
1✔
675
            Command::Pause,
1✔
676
            #[cfg(feature = "inhibit")]
1✔
677
            Command::Delete,
1✔
678
            #[cfg(feature = "inhibit")]
1✔
679
            Command::Resume,
1✔
680
            // c _
1✔
681
            Command::Pause,
1✔
682
            Command::Delete,
1✔
683
            #[cfg(feature = "inhibit")]
1✔
684
            Command::Resume,
1✔
685
            #[cfg(feature = "inhibit")]
1✔
686
            Command::Pause,
1✔
687
            Command::Delete,
1✔
688
            Command::CommitText("ç".to_owned()),
1✔
689
            Command::Resume,
1✔
690
            // c e d
1✔
691
            Command::Pause,
1✔
692
            Command::Delete,
1✔
693
            #[cfg(feature = "inhibit")]
1✔
694
            Command::Resume,
1✔
695
            #[cfg(feature = "inhibit")]
1✔
696
            Command::Pause,
1✔
697
            Command::Delete,
1✔
698
            #[cfg(feature = "inhibit")]
1✔
699
            Command::Resume,
1✔
700
            #[cfg(feature = "inhibit")]
1✔
701
            Command::Pause,
1✔
702
            Command::Delete,
1✔
703
            Command::Delete,
1✔
704
            Command::CommitText("ç".to_owned()),
1✔
705
            Command::Resume,
1✔
706
            // u u
1✔
707
            Command::Pause,
1✔
708
            Command::Delete,
1✔
709
            #[cfg(feature = "inhibit")]
1✔
710
            Command::Resume,
1✔
711
            #[cfg(feature = "inhibit")]
1✔
712
            Command::Pause,
1✔
713
            Command::Delete,
1✔
714
            Command::CommitText("ʉ".to_owned()),
1✔
715
            Command::Resume,
1✔
716
            // a f 3
1✔
717
            Command::Pause,
1✔
718
            Command::Delete,
1✔
719
            #[cfg(feature = "inhibit")]
1✔
720
            Command::Resume,
1✔
721
            #[cfg(feature = "inhibit")]
1✔
722
            Command::Pause,
1✔
723
            Command::Delete,
1✔
724
            #[cfg(feature = "inhibit")]
1✔
725
            Command::Resume,
1✔
726
            #[cfg(feature = "inhibit")]
1✔
727
            Command::Pause,
1✔
728
            Command::Delete,
1✔
729
            Command::Delete,
1✔
730
            Command::CommitText("ʉ\u{304}ɑ\u{304}".to_owned()),
1✔
731
            Command::Resume,
1✔
732
            // a f
1✔
733
            Command::Pause,
1✔
734
            Command::Delete,
1✔
735
            #[cfg(feature = "inhibit")]
1✔
736
            Command::Resume,
1✔
737
            #[cfg(feature = "inhibit")]
1✔
738
            Command::Pause,
1✔
739
            Command::Delete,
1✔
740
            Command::CommitText("ɑ".to_owned()),
1✔
741
            Command::Resume,
1✔
742
            // a f
1✔
743
            Command::Pause,
1✔
744
            Command::Delete,
1✔
745
            #[cfg(feature = "inhibit")]
1✔
746
            Command::Resume,
1✔
747
            #[cfg(feature = "inhibit")]
1✔
748
            Command::Pause,
1✔
749
            Command::Delete,
1✔
750
            Command::CommitText("ɑ".to_owned()),
1✔
751
            Command::Resume,
1✔
752
            // a f
1✔
753
            Command::Pause,
1✔
754
            Command::Delete,
1✔
755
            #[cfg(feature = "inhibit")]
1✔
756
            Command::Resume,
1✔
757
            #[cfg(feature = "inhibit")]
1✔
758
            Command::Pause,
1✔
759
            Command::Delete,
1✔
760
            Command::CommitText("ɑ".to_owned()),
1✔
761
            Command::Resume,
1✔
762
            // f
1✔
763
            Command::Pause,
1✔
764
            Command::Delete,
1✔
765
            Command::Delete,
1✔
766
            Command::CommitText("ɑɑ".to_owned()),
1✔
767
            Command::Resume,
1✔
768
            // 3
1✔
769
            Command::Pause,
1✔
770
            Command::Delete,
1✔
771
            Command::Delete,
1✔
772
            Command::Delete,
1✔
773
            Command::CommitText("ɑ\u{304}ɑ\u{304}".to_owned()),
1✔
774
            Command::Resume,
1✔
775
            // uu
1✔
776
            Command::Pause,
1✔
777
            Command::Delete,
1✔
778
            #[cfg(feature = "inhibit")]
1✔
779
            Command::Resume,
1✔
780
            #[cfg(feature = "inhibit")]
1✔
781
            Command::Pause,
1✔
782
            Command::Delete,
1✔
783
            Command::CommitText("ʉ".to_owned()),
1✔
784
            Command::Resume,
1✔
785
            // 3
1✔
786
            Command::Pause,
1✔
787
            Command::Delete,
1✔
788
            Command::Delete,
1✔
789
            Command::CommitText("ʉ\u{304}".to_owned()),
1✔
790
            Command::Resume,
1✔
791
            // Rollback
1✔
792
            Command::Pause,
1✔
793
            Command::CleanDelete,
1✔
794
            Command::Delete,
1✔
795
            Command::CommitText("ʉ".to_owned()),
1✔
796
            Command::Resume,
1✔
797
            Command::Pause,
1✔
798
            Command::CleanDelete,
1✔
799
            Command::Resume,
1✔
800
            Command::Pause,
1✔
801
            Command::CleanDelete,
1✔
802
            Command::Delete,
1✔
803
            Command::Delete,
1✔
804
            Command::Delete,
1✔
805
            Command::CommitText("ɑɑ".to_owned()),
1✔
806
            Command::Resume,
1✔
807
            Command::Pause,
1✔
808
            Command::CleanDelete,
1✔
809
            Command::Delete,
1✔
810
            Command::CommitText("ɑ".to_owned()),
1✔
811
            Command::Resume,
1✔
812
            Command::Pause,
1✔
813
            Command::CleanDelete,
1✔
814
            Command::Resume,
1✔
815
            Command::Pause,
1✔
816
            Command::CleanDelete,
1✔
817
            Command::Resume,
1✔
818
            Command::Pause,
1✔
819
            Command::CleanDelete,
1✔
820
            Command::Resume,
1✔
821
            Command::Pause,
1✔
822
            Command::CleanDelete,
1✔
823
            Command::Delete,
1✔
824
            Command::Delete,
1✔
825
            Command::Delete,
1✔
826
            Command::CommitText("ʉ".to_owned()),
1✔
827
            Command::Resume,
1✔
828
            Command::Pause,
1✔
829
            Command::CleanDelete,
1✔
830
            Command::Resume,
1✔
831
            Command::Pause,
1✔
832
            Command::CleanDelete,
1✔
833
            Command::CommitText("ç".to_owned()),
1✔
834
            Command::Resume,
1✔
835
            Command::Pause,
1✔
836
            Command::CleanDelete,
1✔
837
            Command::Resume,
1✔
838
            Command::Pause,
1✔
839
            Command::CleanDelete,
1✔
840
            Command::Resume,
1✔
841
        ]);
1✔
842

843
        while let Some(command) = preprocessor.pop_queue() {
121✔
844
            assert_eq!(command, expecteds.pop_front().unwrap());
120✔
845
        }
846
    }
1✔
847
}
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