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

corebreaker / poreader / 178

03 May 2024 05:07AM UTC coverage: 98.197% (-1.6%) from 99.764%
178

push

circleci

web-flow
Merge pull request #13 from corebreaker/circleci-project-setup

Fix coverage check in CI

708 of 721 relevant lines covered (98.2%)

50.92 hits per line

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

98.02
/src/plural/formula/node.rs
1
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
12✔
2
pub(crate) enum UnOp {
3
    Neg,
4
    Not,
5
}
6

7
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
133✔
8
pub(crate) enum BinOp {
9
    Add,
10
    Sub,
11
    Mul,
12
    Div,
13
    Mod,
14
    And,
15
    Or,
16
    Eq,
17
    Ne,
18
    Lt,
19
    Lte,
20
    Gt,
21
    Gte,
22
}
23

24
#[inline]
25
fn bool_to_num(b: bool) -> i64 {
451✔
26
    if b {
451✔
27
        1
215✔
28
    } else {
29
        0
236✔
30
    }
31
}
451✔
32

33
#[inline]
34
fn get_infinity(v: i64) -> i64 {
3✔
35
    if v < 0 {
3✔
36
        i64::MIN
1✔
37
    } else {
38
        i64::MAX
2✔
39
    }
40
}
3✔
41

42
#[derive(Clone, Debug)]
146✔
43
pub(crate) enum Node {
44
    Var,
45
    Num(i64),
43✔
46
    UnOp {
47
        op: UnOp,
3✔
48
        rhs: Box<Node>,
3✔
49
    },
50
    BinOp {
51
        op: BinOp,
57✔
52
        lhs: Box<Node>,
57✔
53
        rhs: Box<Node>,
57✔
54
    },
55
    Cond {
56
        test: Box<Node>,
11✔
57
        if_true: Box<Node>,
11✔
58
        if_false: Box<Node>,
8✔
59
    },
60
}
61

62
impl Node {
63
    pub(super) fn new_num(v: i64) -> Node {
386✔
64
        Node::Num(v)
386✔
65
    }
772✔
66

67
    pub(super) fn new_unop(op: UnOp, rhs: Node) -> Node {
14✔
68
        Node::UnOp { op, rhs: Box::new(rhs) }
14✔
69
    }
14✔
70

71
    pub(super) fn new_binop(op: BinOp, lhs: Node, rhs: Node) -> Node {
367✔
72
        Node::BinOp {
367✔
73
            op,
74
            lhs: Box::new(lhs),
367✔
75
            rhs: Box::new(rhs),
367✔
76
        }
×
77
    }
367✔
78

79
    pub(super) fn new_cond(test: Node, if_true: Node, if_false: Node) -> Node {
59✔
80
        Node::Cond {
59✔
81
            test: Box::new(test),
59✔
82
            if_true: Box::new(if_true),
59✔
83
            if_false: Box::new(if_false),
59✔
84
        }
×
85
    }
59✔
86

87
    pub(super) fn execute(&self, n: i64) -> i64 {
1,815✔
88
        match self {
1,815✔
89
            Node::Var => n,
373✔
90
            Node::Num(v) => *v,
603✔
91
            Node::UnOp { op, rhs } => match op {
26✔
92
                UnOp::Not => bool_to_num(rhs.execute(n) == 0),
15✔
93
                UnOp::Neg => -rhs.execute(n),
11✔
94
            },
95
            Node::BinOp { op, lhs, rhs } => {
651✔
96
                let lhs = lhs.execute(n);
651✔
97

98
                match op {
651✔
99
                    BinOp::Add => lhs.overflowing_add(rhs.execute(n)).0,
10✔
100
                    BinOp::Sub => lhs.overflowing_sub(rhs.execute(n)).0,
18✔
101
                    BinOp::Mul => lhs.overflowing_mul(rhs.execute(n)).0,
5✔
102
                    BinOp::Div => {
103
                        let rhs = rhs.execute(n);
11✔
104

105
                        if rhs != 0 {
11✔
106
                            lhs.overflowing_div(rhs).0
10✔
107
                        } else {
108
                            get_infinity(lhs)
1✔
109
                        }
110
                    }
111
                    BinOp::Mod => {
173✔
112
                        let rhs = rhs.execute(n);
173✔
113

114
                        if rhs != 0 {
173✔
115
                            lhs.overflowing_rem(rhs).0
168✔
116
                        } else {
117
                            lhs
5✔
118
                        }
119
                    }
120
                    BinOp::And => bool_to_num((lhs != 0) && (rhs.execute(n) != 0)),
101✔
121
                    BinOp::Or => bool_to_num((lhs != 0) || (rhs.execute(n) != 0)),
32✔
122
                    BinOp::Eq => bool_to_num(lhs == rhs.execute(n)),
76✔
123
                    BinOp::Ne => bool_to_num(lhs != rhs.execute(n)),
28✔
124
                    BinOp::Lt => bool_to_num(lhs < rhs.execute(n)),
61✔
125
                    BinOp::Lte => bool_to_num(lhs <= rhs.execute(n)),
15✔
126
                    BinOp::Gt => bool_to_num(lhs > rhs.execute(n)),
53✔
127
                    BinOp::Gte => bool_to_num(lhs >= rhs.execute(n)),
68✔
128
                }
129
            }
130
            Node::Cond {
131
                test,
162✔
132
                if_true,
162✔
133
                if_false,
162✔
134
            } => {
135
                if test.execute(n) != 0 {
324✔
136
                    if_true.execute(n)
66✔
137
                } else {
138
                    if_false.execute(n)
96✔
139
                }
140
            }
141
        }
142
    }
1,815✔
143
}
144

145
impl PartialEq<Self> for Node {
146
    fn eq(&self, other: &Self) -> bool {
162✔
147
        match (self, other) {
162✔
148
            (Node::Var, Node::Var) => true,
38✔
149
            (Node::Num(l), Node::Num(r)) => l == r,
56✔
150
            (Node::UnOp { op: lo, rhs: lr }, Node::UnOp { op: ro, rhs: rr }) => (lo == ro) && (lr == rr),
4✔
151
            (
51✔
152
                Node::BinOp {
153
                    op: lo,
51✔
154
                    lhs: ll,
51✔
155
                    rhs: lr,
51✔
156
                },
157
                Node::BinOp {
158
                    op: ro,
51✔
159
                    lhs: rl,
51✔
160
                    rhs: rr,
51✔
161
                },
162
            ) => (lo == ro) && (ll == rl) && (lr == rr),
51✔
163
            (
9✔
164
                Node::Cond {
165
                    test: lc,
9✔
166
                    if_true: lt,
9✔
167
                    if_false: lf,
9✔
168
                },
169
                Node::Cond {
170
                    test: rc,
9✔
171
                    if_true: rt,
9✔
172
                    if_false: rf,
9✔
173
                },
174
            ) => (lc == rc) && (lt == rt) && (lf == rf),
9✔
175
            _ => false,
4✔
176
        }
177
    }
162✔
178
}
179

180
impl Eq for Node {}
181

182
// no-coverage:start
183
#[cfg(test)]
184
mod tests {
185
    use super::*;
186
    use std::collections::HashMap;
187

188
    struct TestCase {
189
        test_name: &'static str,
190
        node: Node,
191
        exec_cases: HashMap<i64, i64>,
192
    }
193

194
    impl TestCase {
195
        fn make_tests() -> Vec<Self> {
196
            vec![
197
                TestCase {
198
                    test_name: "Variable",
199
                    node: Node::Var,
200
                    exec_cases: vec![(-100, -100), (-10, -10), (100, 100)].into_iter().collect(),
201
                },
202
                TestCase {
203
                    test_name: "Constant",
204
                    node: Node::new_num(100),
205
                    exec_cases: vec![(-100, 100), (-10, 100), (0, 100), (5, 100), (100, 100)]
206
                        .into_iter()
207
                        .collect(),
208
                },
209
                TestCase {
210
                    test_name: "Operator `+`",
211
                    node: Node::new_binop(BinOp::Add, Node::Var, Node::new_num(10)),
212
                    exec_cases: vec![(-100, -90), (-10, 0), (100, 110)].into_iter().collect(),
213
                },
214
                TestCase {
215
                    test_name: "Operator `-`",
216
                    node: Node::new_binop(BinOp::Sub, Node::Var, Node::new_num(10)),
217
                    exec_cases: vec![(-100, -110), (5, -5), (10, 0), (100, 90)].into_iter().collect(),
218
                },
219
                TestCase {
220
                    test_name: "Operator `*`",
221
                    node: Node::new_binop(BinOp::Mul, Node::Var, Node::new_num(10)),
222
                    exec_cases: vec![(-2, -20), (0, 0), (5, 50)].into_iter().collect(),
223
                },
224
                TestCase {
225
                    test_name: "Operator `/`",
226
                    node: Node::new_binop(BinOp::Div, Node::Var, Node::new_num(10)),
227
                    exec_cases: vec![(-2, 0), (-20, -2), (0, 0), (20, 2), (35, 3)].into_iter().collect(),
228
                },
229
                TestCase {
230
                    test_name: "Operator `/` (inverse)",
231
                    node: Node::new_binop(BinOp::Div, Node::new_num(1000), Node::Var),
232
                    exec_cases: vec![(0, i64::MAX), (-10, -100), (100, 10)].into_iter().collect(),
233
                },
234
                TestCase {
235
                    test_name: "Operator `%`",
236
                    node: Node::new_binop(BinOp::Mod, Node::Var, Node::new_num(10)),
237
                    exec_cases: vec![(-12, -2), (-10, 0), (0, 0), (23, 3), (35, 5)]
238
                        .into_iter()
239
                        .collect(),
240
                },
241
                TestCase {
242
                    test_name: "Operator `%` (with zero)",
243
                    node: Node::new_binop(BinOp::Mod, Node::Var, Node::new_num(0)),
244
                    exec_cases: vec![(-12, -12), (-10, -10), (0, 0), (23, 23), (35, 35)]
245
                        .into_iter()
246
                        .collect(),
247
                },
248
                TestCase {
249
                    test_name: "Operator `==`",
250
                    node: Node::new_binop(BinOp::Eq, Node::Var, Node::new_num(10)),
251
                    exec_cases: vec![(-12, 0), (2, 0), (100, 0), (10, 1)].into_iter().collect(),
252
                },
253
                TestCase {
254
                    test_name: "Operator `!=`",
255
                    node: Node::new_binop(BinOp::Ne, Node::Var, Node::new_num(10)),
256
                    exec_cases: vec![(-12, 1), (2, 1), (100, 1), (10, 0)].into_iter().collect(),
257
                },
258
                TestCase {
259
                    test_name: "Operator `<`",
260
                    node: Node::new_binop(BinOp::Lt, Node::Var, Node::new_num(10)),
261
                    exec_cases: vec![(-12, 1), (2, 1), (100, 0), (10, 0)].into_iter().collect(),
262
                },
263
                TestCase {
264
                    test_name: "Operator `<=`",
265
                    node: Node::new_binop(BinOp::Lte, Node::Var, Node::new_num(10)),
266
                    exec_cases: vec![(-12, 1), (2, 1), (100, 0), (10, 1)].into_iter().collect(),
267
                },
268
                TestCase {
269
                    test_name: "Operator `>`",
270
                    node: Node::new_binop(BinOp::Gt, Node::Var, Node::new_num(10)),
271
                    exec_cases: vec![(-12, 0), (2, 0), (100, 1), (10, 0)].into_iter().collect(),
272
                },
273
                TestCase {
274
                    test_name: "Operator `>=`",
275
                    node: Node::new_binop(BinOp::Gte, Node::Var, Node::new_num(10)),
276
                    exec_cases: vec![(-12, 0), (2, 0), (100, 1), (10, 1)].into_iter().collect(),
277
                },
278
                TestCase {
279
                    test_name: "Operator `!` (not)",
280
                    node: Node::new_unop(UnOp::Not, Node::Var),
281
                    exec_cases: vec![(-12, 0), (100, 0), (0, 1)].into_iter().collect(),
282
                },
283
                TestCase {
284
                    test_name: "Operator 'neg'",
285
                    node: Node::new_unop(UnOp::Neg, Node::Var),
286
                    exec_cases: vec![(-12, 12), (100, -100), (0, 0)].into_iter().collect(),
287
                },
288
                TestCase {
289
                    test_name: "Operator `&&`",
290
                    node: Node::new_binop(
291
                        BinOp::And,
292
                        Node::new_binop(BinOp::Lt, Node::new_num(-5), Node::Var),
293
                        Node::new_binop(BinOp::Lte, Node::Var, Node::new_num(25)),
294
                    ),
295
                    exec_cases: vec![(-12, 0), (100, 0), (0, 1), (-3, 1), (10, 1), (-5, 0), (25, 1)]
296
                        .into_iter()
297
                        .collect(),
298
                },
299
                TestCase {
300
                    test_name: "Operator `||`",
301
                    node: Node::new_binop(
302
                        BinOp::Or,
303
                        Node::new_binop(BinOp::Gte, Node::new_num(-5), Node::Var),
304
                        Node::new_binop(BinOp::Gt, Node::Var, Node::new_num(25)),
305
                    ),
306
                    exec_cases: vec![(-12, 1), (100, 1), (0, 0), (-3, 0), (10, 0), (-5, 1), (25, 0)]
307
                        .into_iter()
308
                        .collect(),
309
                },
310
                TestCase {
311
                    test_name: "Expression with `?`",
312
                    node: Node::new_cond(
313
                        Node::new_binop(BinOp::Lt, Node::Var, Node::new_num(10)),
314
                        Node::new_num(1),
315
                        Node::new_num(2),
316
                    ),
317
                    exec_cases: vec![(-12, 1), (100, 2), (0, 1), (-3, 1), (10, 2)].into_iter().collect(),
318
                },
319
                TestCase {
320
                    test_name: "Big expression",
321
                    node: Node::new_cond(
322
                        Node::new_binop(BinOp::Gt, Node::Var, Node::new_num(10)),
323
                        Node::new_cond(
324
                            Node::new_binop(
325
                                BinOp::Eq,
326
                                Node::new_binop(BinOp::Mod, Node::Var, Node::new_num(10)),
327
                                Node::new_num(3),
328
                            ),
329
                            Node::new_num(10),
330
                            Node::new_cond(
331
                                Node::new_binop(BinOp::Lt, Node::Var, Node::new_num(100)),
332
                                Node::new_num(20),
333
                                Node::new_cond(
334
                                    Node::new_unop(
335
                                        UnOp::Not,
336
                                        Node::new_binop(BinOp::Gt, Node::Var, Node::new_num(200)),
337
                                    ),
338
                                    Node::new_binop(
339
                                        BinOp::Add,
340
                                        Node::new_unop(UnOp::Neg, Node::Var),
341
                                        Node::new_num(1000),
342
                                    ),
343
                                    Node::new_num(1234),
344
                                ),
345
                            ),
346
                        ),
347
                        Node::new_binop(BinOp::Sub, Node::Var, Node::new_num(10)),
348
                    ),
349
                    exec_cases: vec![
350
                        (-12, -22),
351
                        (0, -10),
352
                        (10, 0),
353
                        (43, 10),
354
                        (53, 10),
355
                        (55, 20),
356
                        (44, 20),
357
                        (441, 1234),
358
                        (404, 1234),
359
                        (150, 850),
360
                        (156, 844),
361
                        (200, 800),
362
                    ]
363
                    .into_iter()
364
                    .collect(),
365
                },
366
            ]
367
        }
368

369
        fn run(&self) {
370
            let prefix = format!("For test {}", self.test_name);
371

372
            for (count, expected) in self.exec_cases.iter() {
373
                let res = self.node.execute(*count);
374

375
                assert_eq!(&res, expected, "{}, bad execution result for count {}", prefix, count);
376
            }
377
        }
378
    }
379

380
    #[test]
381
    fn test_enum_node() {
382
        assert_ne!(Node::Num(0).clone(), Node::Var.clone());
383
        assert_eq!(format!("{:?}", Node::Var), String::from("Var"));
384
        assert_eq!(format!("{:?}", Node::Num(0)), String::from("Num(0)"));
385

386
        let mut n: Node;
387

388
        n = Node::UnOp {
389
            op: UnOp::Not,
390
            rhs: Box::new(Node::Var),
391
        };
392
        assert_ne!(n.clone(), Node::Var);
393
        assert_eq!(format!("{:?}", n), String::from("UnOp { op: Not, rhs: Var }"));
394

395
        n = Node::BinOp {
396
            op: BinOp::Add,
397
            lhs: Box::new(Node::Var),
398
            rhs: Box::new(Node::Var),
399
        };
400
        assert_ne!(n.clone(), Node::Var);
401
        assert_eq!(
402
            format!("{:?}", n),
403
            String::from("BinOp { op: Add, lhs: Var, rhs: Var }")
404
        );
405

406
        n = Node::Cond {
407
            test: Box::new(Node::Var),
408
            if_true: Box::new(Node::Var),
409
            if_false: Box::new(Node::Var),
410
        };
411
        assert_ne!(n.clone(), Node::Var);
412
        assert_eq!(
413
            format!("{:?}", n),
414
            String::from("Cond { test: Var, if_true: Var, if_false: Var }")
415
        );
416
    }
417

418
    macro_rules! check_enum_variant {
419
        ($enum:ident, $variant:ident) => {
420
            assert_eq!($enum::$variant.clone(), $enum::$variant);
421
            assert_eq!(format!("{:?}", $enum::$variant), stringify!($variant));
422
        };
423
    }
424

425
    #[test]
426
    fn test_enum_unop() {
427
        check_enum_variant!(UnOp, Not);
428
        check_enum_variant!(UnOp, Neg);
429
    }
430

431
    #[test]
432
    fn test_enum_binop() {
433
        check_enum_variant!(BinOp, Add);
434
        check_enum_variant!(BinOp, Sub);
435
        check_enum_variant!(BinOp, Mul);
436
        check_enum_variant!(BinOp, Div);
437
        check_enum_variant!(BinOp, Mod);
438
        check_enum_variant!(BinOp, And);
439
        check_enum_variant!(BinOp, Or);
440
        check_enum_variant!(BinOp, Eq);
441
        check_enum_variant!(BinOp, Ne);
442
        check_enum_variant!(BinOp, Lt);
443
        check_enum_variant!(BinOp, Lte);
444
        check_enum_variant!(BinOp, Gt);
445
        check_enum_variant!(BinOp, Gte);
446
    }
447

448
    #[test]
449
    fn test_func_get_infinity() {
450
        assert_eq!(get_infinity(10), i64::MAX);
451
        assert_eq!(get_infinity(-10), i64::MIN);
452
    }
453

454
    #[test]
455
    fn test_func_bool_to_num() {
456
        assert_eq!(bool_to_num(false), 0);
457
        assert_eq!(bool_to_num(true), 1);
458
    }
459

460
    #[test]
461
    fn execute_nodes() {
462
        TestCase::make_tests().into_iter().for_each(|t| t.run());
463
    }
464
}
465
// no-coverage:stop
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