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

ergoplatform / sigma-rust / 8953175335

04 May 2024 08:51PM UTC coverage: 80.473% (+0.1%) from 80.331%
8953175335

Pull #736

github

web-flow
Merge 0fdf2d258 into 57a105462
Pull Request #736: Transaction Validation

165 of 228 new or added lines in 15 files covered. (72.37%)

8 existing lines in 2 files now uncovered.

10723 of 13325 relevant lines covered (80.47%)

3.29 hits per line

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

75.0
/ergotree-interpreter/src/eval/scoll.rs
1
use crate::eval::EvalError;
2
use crate::eval::Evaluable;
3

4
use ergotree_ir::mir::constant::TryExtractInto;
5
use ergotree_ir::mir::expr::Expr;
6
use ergotree_ir::mir::value::CollKind;
7
use ergotree_ir::mir::value::Value;
8
use ergotree_ir::types::stuple::STuple;
9
use ergotree_ir::types::stype::SType::SInt;
10

11
use super::EvalFn;
12
use std::convert::TryFrom;
13

14
pub(crate) static INDEX_OF_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
1✔
15
    Ok(Value::Int({
2✔
16
        let normalized_input_vals: Vec<Value> = match obj {
3✔
17
            Value::Coll(coll) => Ok(coll.as_vec()),
1✔
18
            _ => Err(EvalError::UnexpectedValue(format!(
×
19
                "expected obj to be Value::Coll, got: {0:?}",
20
                obj
21
            ))),
22
        }?;
23
        let target_element = args
2✔
24
            .first()
25
            .cloned()
26
            .ok_or_else(|| EvalError::NotFound("indexOf: missing first arg".to_string()))?;
×
27
        let from = args
2✔
28
            .get(1)
29
            .cloned()
NEW
30
            .ok_or_else(|| EvalError::NotFound("indexOf: missing second arg".to_string()))?
×
NEW
31
            .try_extract_into::<i32>()?
×
32
            .max(0);
33

34
        normalized_input_vals
4✔
35
            .into_iter()
36
            .skip(from as usize)
1✔
37
            .position(|it| it == target_element)
3✔
38
            .map(|idx| idx as i32 + from)
3✔
39
            .unwrap_or(-1)
40
    }))
41
};
42

43
pub(crate) static FLATMAP_EVAL_FN: EvalFn = |env, ctx, obj, args| {
1✔
44
    let input_v = obj;
1✔
45
    let lambda_v = args
2✔
46
        .first()
47
        .cloned()
48
        .ok_or_else(|| EvalError::NotFound("flatmap: eval is missing first arg".to_string()))?;
×
49
    let input_v_clone = input_v.clone();
1✔
50
    let lambda = match &lambda_v {
3✔
51
        Value::Lambda(l) => Ok(l),
1✔
52
        _ => Err(EvalError::UnexpectedValue(format!(
×
53
            "expected lambda to be Value::FuncValue got: {0:?}",
54
            input_v_clone
55
        ))),
56
    }?;
57
    if lambda.args.len() > 1 {
2✔
58
        return Err(EvalError::UnexpectedValue(format!(
×
59
            "flatmap: expected lambda taking 1 arg but got {} args",
60
            lambda.args.len()
×
61
        )));
62
    }
63
    let unsupported_msg =
64
        "unsupported lambda in flatMap: allowed usage `xs.flatMap(x => x.property)".to_string();
2✔
65
    if let Expr::MethodCall(mc) = &*lambda.body {
1✔
66
        if !mc.expr().args.is_empty() {
2✔
NEW
67
            return Err(EvalError::UnexpectedValue(unsupported_msg));
×
68
        }
69
    }
70
    let mut lambda_call = |arg: Value| {
2✔
71
        let func_arg = lambda.args.first().ok_or_else(|| {
2✔
72
            EvalError::NotFound("flatmap: lambda has empty arguments list".to_string())
×
73
        })?;
74
        let orig_val = env.get(func_arg.idx).cloned();
2✔
75
        env.insert(func_arg.idx, arg);
1✔
76
        let res = lambda.body.eval(env, ctx);
1✔
77
        if let Some(orig_val) = orig_val {
1✔
78
            env.insert(func_arg.idx, orig_val);
×
79
        } else {
80
            env.remove(&func_arg.idx);
2✔
81
        }
82
        res
1✔
83
    };
84
    let mapper_input_tpe = lambda
2✔
85
        .args
86
        .first()
87
        .cloned()
88
        .map(|arg| arg.tpe)
2✔
89
        .ok_or_else(|| {
×
90
            EvalError::NotFound(
×
91
                "flatmap: lambda args are empty (does not have arguments)".to_string(),
×
92
            )
93
        })?;
94
    let normalized_input_vals: Vec<Value> = match input_v {
3✔
95
        Value::Coll(coll) => {
1✔
96
            if *coll.elem_tpe() != mapper_input_tpe {
2✔
97
                return Err(EvalError::UnexpectedValue(format!(
×
98
                    "expected Flatmap input element type to be {0:?}, got: {1:?}",
99
                    mapper_input_tpe,
100
                    coll.elem_tpe()
×
101
                )));
102
            };
103
            Ok(coll.as_vec())
2✔
104
        }
105
        _ => Err(EvalError::UnexpectedValue(format!(
×
106
            "expected Flatmap input to be Value::Coll, got: {0:?}",
107
            input_v
108
        ))),
109
    }?;
110
    normalized_input_vals
4✔
111
        .iter()
112
        .map(|item| lambda_call(item.clone()))
3✔
113
        .collect::<Result<Vec<Value>, EvalError>>()
114
        .map(|values| {
2✔
115
            CollKind::from_vec_vec(lambda.body.tpe(), values).map_err(EvalError::TryExtractFrom)
2✔
116
        })
117
        .and_then(|v| v) // flatten <Result<Result<Value, _>, _>
2✔
118
        .map(Value::Coll)
119
};
120

121
pub(crate) static ZIP_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
1✔
122
    let (type_1, coll_1) = match obj {
3✔
123
        Value::Coll(coll) => Ok((coll.elem_tpe().clone(), coll.as_vec())),
1✔
124
        _ => Err(EvalError::UnexpectedValue(format!(
×
125
            "expected obj to be Value::Coll, got: {0:?}",
126
            obj
127
        ))),
128
    }?;
129
    let arg_1 = args
2✔
130
        .first()
131
        .cloned()
132
        .ok_or_else(|| EvalError::NotFound("zip: missing first arg".to_string()))?;
×
133
    let (type_2, coll_2) = match arg_1 {
3✔
134
        Value::Coll(coll) => Ok((coll.elem_tpe().clone(), coll.as_vec())),
1✔
135
        _ => Err(EvalError::UnexpectedValue(format!(
×
136
            "expected first arg to be Value::Coll, got: {0:?}",
137
            arg_1
138
        ))),
139
    }?;
140
    let zip = coll_1
2✔
141
        .into_iter()
142
        .zip(coll_2)
1✔
143
        .map(|(a, b)| Value::Tup([a, b].into()))
2✔
144
        .collect::<Vec<Value>>();
1✔
145
    let coll_zip = CollKind::from_vec(STuple::pair(type_1, type_2).into(), zip);
2✔
146
    match coll_zip {
1✔
147
        Ok(coll) => Ok(Value::Coll(coll)),
1✔
148
        Err(e) => Err(EvalError::TryExtractFrom(e)),
×
149
    }
150
};
151

152
pub(crate) static INDICES_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
1✔
153
    let normalized_input_vals: Vec<Value> = match obj {
3✔
154
        Value::Coll(coll) => Ok(coll.as_vec()),
1✔
155
        _ => Err(EvalError::UnexpectedValue(format!(
×
156
            "expected obj to be Value::Coll, got: {0:?}",
157
            obj
158
        ))),
159
    }?;
160
    let indices_i32 = normalized_input_vals
2✔
161
        .into_iter()
162
        .enumerate()
163
        .map(|(i, _)| i32::try_from(i))
2✔
164
        .collect::<Result<Vec<i32>, _>>();
165
    let indices_val =
166
        indices_i32.map(|vec_i32| vec_i32.into_iter().map(Value::Int).collect::<Vec<Value>>());
3✔
167
    match indices_val {
1✔
168
        Ok(vec_val) => match CollKind::from_vec(SInt, vec_val) {
2✔
169
            Ok(coll) => Ok(Value::Coll(coll)),
1✔
170
            Err(e) => Err(EvalError::TryExtractFrom(e)),
×
171
        },
172
        Err(e) => Err(EvalError::UnexpectedValue(format!(
1✔
173
            "Coll length overflow: {0:?}",
174
            e
175
        ))),
176
    }
177
};
178

179
pub(crate) static PATCH_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
1✔
180
    let (input_tpe, normalized_input_vals) = match obj {
3✔
181
        Value::Coll(coll) => Ok((coll.elem_tpe().clone(), coll.as_vec())),
1✔
182
        _ => Err(EvalError::UnexpectedValue(format!(
×
183
            "expected obj to be Value::Coll, got: {0:?}",
184
            obj
185
        ))),
186
    }?;
187
    let from_index_val = args
2✔
188
        .first()
189
        .cloned()
190
        .ok_or_else(|| EvalError::NotFound("patch: missing first arg (from)".to_string()))?;
×
191
    let patch_val = args
2✔
192
        .get(1)
193
        .cloned()
194
        .ok_or_else(|| EvalError::NotFound("patch: missing second arg (patch)".to_string()))?;
×
195
    let replaced_val = args
2✔
196
        .get(2)
197
        .cloned()
198
        .ok_or_else(|| EvalError::NotFound("patch: missing third arg (replaced)".to_string()))?;
×
199

200
    let from = from_index_val.try_extract_into::<i32>()?.max(0) as usize;
2✔
201
    let replaced = replaced_val.try_extract_into::<i32>()?.max(0) as usize;
1✔
202
    let patch = match patch_val {
3✔
203
        Value::Coll(coll) => Ok(coll.as_vec()),
1✔
204
        _ => Err(EvalError::UnexpectedValue(format!(
×
205
            "expected patch arg to be Value::Coll, got: {0:?}",
206
            patch_val
207
        ))),
208
    }?;
209

210
    let res = normalized_input_vals
4✔
211
        .iter()
212
        .take(from)
213
        .chain(patch.iter())
1✔
214
        .chain(normalized_input_vals.iter().skip(from + replaced))
1✔
215
        .cloned()
216
        .collect();
217
    Ok(Value::Coll(CollKind::from_vec(input_tpe, res)?))
1✔
218
};
219

220
pub(crate) static UPDATED_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
1✔
221
    let (input_tpe, normalized_input_vals) = match obj {
3✔
222
        Value::Coll(coll) => Ok((coll.elem_tpe().clone(), coll.as_vec())),
1✔
223
        _ => Err(EvalError::UnexpectedValue(format!(
×
224
            "expected obj to be Value::Coll, got: {0:?}",
225
            obj
226
        ))),
227
    }?;
228

229
    let target_index_val = args
2✔
230
        .first()
231
        .cloned()
232
        .ok_or_else(|| EvalError::NotFound("updated: missing first arg (index)".to_string()))?;
×
233
    let update_val = args
2✔
234
        .get(1)
235
        .cloned()
236
        .ok_or_else(|| EvalError::NotFound("updated: missing second arg (update)".to_string()))?;
×
237

238
    let target_index_usize = target_index_val.clone().try_extract_into::<i32>()? as usize;
2✔
239
    let mut res = normalized_input_vals;
1✔
240

241
    match res.get_mut(target_index_usize) {
2✔
242
        Some(elem) => {
1✔
243
            *elem = update_val;
1✔
244
            Ok(Value::Coll(CollKind::from_vec(input_tpe, res)?))
1✔
245
        }
246
        None => Err(EvalError::UnexpectedValue(format!(
1✔
247
            "updated: target index out of bounds, got: {:?}",
248
            target_index_val
249
        ))),
250
    }
251
};
252

253
pub(crate) static UPDATE_MANY_EVAL_FN: EvalFn =
254
    |_env, _ctx, obj, args| {
1✔
255
        let (input_tpe, normalized_input_vals) = match obj {
3✔
256
            Value::Coll(coll) => Ok((coll.elem_tpe().clone(), coll.as_vec())),
1✔
257
            _ => Err(EvalError::UnexpectedValue(format!(
×
258
                "expected obj to be Value::Coll, got: {0:?}",
259
                obj
260
            ))),
261
        }?;
262

263
        let indexes_arg = args.first().cloned().ok_or_else(|| {
2✔
264
            EvalError::NotFound("updated: missing first arg (indexes)".to_string())
×
265
        })?;
266
        let updates_arg = args.get(1).cloned().ok_or_else(|| {
2✔
267
            EvalError::NotFound("updated: missing second arg (updates)".to_string())
×
268
        })?;
269

270
        let (updates_tpe, updates_val) = match updates_arg {
3✔
271
            Value::Coll(coll) => Ok((coll.elem_tpe().clone(), coll.as_vec())),
1✔
272
            _ => Err(EvalError::UnexpectedValue(format!(
×
273
                "expected first arg to be Value::Coll, got: {0:?}",
274
                updates_arg
275
            ))),
276
        }?;
277

278
        let indexes_usize = indexes_arg
2✔
279
            .try_extract_into::<Vec<i32>>()?
×
280
            .into_iter()
281
            .map(|i| i as usize)
2✔
282
            .collect::<Vec<usize>>();
283

284
        let inputs_len = normalized_input_vals.len();
2✔
285
        let indexes_len = indexes_usize.len();
1✔
286
        let updates_len = updates_val.len();
1✔
287

288
        if indexes_len != updates_len {
1✔
289
            return Err(EvalError::UnexpectedValue(format!(
1✔
290
                "Collections should have same length but was: \
291
            {0:?} and {1:?}. \n Indexes: {2:?} \n Updates: {3:?}",
292
                indexes_len, updates_len, indexes_usize, updates_val
293
            )));
294
        };
295
        if input_tpe != updates_tpe {
2✔
296
            return Err(EvalError::UnexpectedValue(format!(
×
297
                "Collections should be same type but was: \
298
            {0:?} and {1:?}. \n Inputs: {2:?} \n Updates: {3:?}",
299
                input_tpe, updates_tpe, normalized_input_vals, updates_val
300
            )));
301
        };
302

303
        let mut i = 0;
1✔
304
        let mut res = normalized_input_vals;
1✔
305

306
        while i < indexes_len {
3✔
307
            let pos = indexes_usize[i];
2✔
308
            if pos >= inputs_len {
1✔
309
                return Err(EvalError::UnexpectedValue(format!(
1✔
310
                    "updateMany index out of bounds, got: {0:?}",
311
                    pos
312
                )));
313
            }
314
            let update = updates_val[i].clone();
2✔
315
            match res.get_mut(pos) {
2✔
316
                Some(elem) => *elem = update,
1✔
317
                None => {
318
                    return Err(EvalError::UnexpectedValue(format!(
×
319
                        "updateMany index out of bounds, got: {0:?}",
320
                        pos
321
                    )))
322
                }
323
            }
324
            i += 1;
1✔
325
        }
326
        Ok(Value::Coll(CollKind::from_vec(input_tpe, res)?))
2✔
327
    };
328

329
#[allow(clippy::unwrap_used)]
330
#[cfg(test)]
331
#[cfg(feature = "arbitrary")]
332
mod tests {
333
    use ergotree_ir::mir::constant::Constant;
334
    use ergotree_ir::mir::constant::Literal;
335
    use ergotree_ir::mir::expr::Expr;
336
    use ergotree_ir::mir::func_value::FuncArg;
337
    use ergotree_ir::mir::func_value::FuncValue;
338
    use ergotree_ir::mir::method_call::MethodCall;
339
    use ergotree_ir::mir::val_use::ValUse;
340
    use ergotree_ir::mir::value::CollKind;
341
    use ergotree_ir::types::scoll;
342
    use ergotree_ir::types::stype::SType;
343
    use ergotree_ir::types::stype_param::STypeVar;
344

345
    use crate::eval::tests::{eval_out_wo_ctx, try_eval_out_wo_ctx};
346

347
    #[test]
348
    fn eval_index_of() {
349
        let index_of_expr = |coll: Vec<i64>, elem: i64, from: i32| -> Expr {
350
            MethodCall::new(
351
                coll.into(),
352
                scoll::INDEX_OF_METHOD.clone().with_concrete_types(
353
                    &[(STypeVar::t(), SType::SLong)].iter().cloned().collect(),
354
                ),
355
                vec![elem.into(), from.into()],
356
            )
357
            .unwrap()
358
            .into()
359
        };
360
        let res = eval_out_wo_ctx::<i32>(&index_of_expr(vec![1i64, 2i64], 2, 0));
361
        assert_eq!(res, 1);
362
        // Test searching in array starting from 1st index
363
        let res = eval_out_wo_ctx::<i32>(&index_of_expr(vec![1i64, 2i64], 2, 1));
364
        assert_eq!(res, 1);
365

366
        // Test searching in array starting from 1st index
367
        let res = eval_out_wo_ctx::<i32>(&index_of_expr(vec![1i64, 2i64], 2, 1));
368
        assert_eq!(res, 1);
369
        // Test searching in array starting from index greater than array length
370
        let res = eval_out_wo_ctx::<i32>(&index_of_expr(vec![1i64, 2i64], 2, 10000));
371
        assert_eq!(res, -1);
372
        // Test element that doesn't exist
373
        let res = eval_out_wo_ctx::<i32>(&index_of_expr(vec![1i64, 2i64], 3, 0));
374
        assert_eq!(res, -1);
375
    }
376

377
    #[test]
378
    fn eval_index_of_default() {
379
        let coll_const: Constant = vec![1i64, 2i64].into();
380
        let expr: Expr = MethodCall::new(
381
            coll_const.into(),
382
            scoll::INDEX_OF_METHOD
383
                .clone()
384
                .with_concrete_types(&[(STypeVar::t(), SType::SLong)].iter().cloned().collect()),
385
            vec![3i64.into(), 0i32.into()],
386
        )
387
        .unwrap()
388
        .into();
389
        let res = eval_out_wo_ctx::<i32>(&expr);
390
        assert_eq!(res, -1);
391
    }
392

393
    #[test]
394
    fn eval_flatmap() {
395
        let coll_const = Constant {
396
            tpe: SType::SColl(Box::new(SType::SColl(Box::new(SType::SLong)))),
397
            v: Literal::Coll(CollKind::WrappedColl {
398
                items: vec![vec![4i64, 5i64].into(), vec![3i64].into()],
399
                elem_tpe: SType::SColl(Box::new(SType::SLong)),
400
            }),
401
        };
402
        let body: Expr = MethodCall::new(
403
            ValUse {
404
                val_id: 1.into(),
405
                tpe: SType::SColl(Box::new(SType::SLong)),
406
            }
407
            .into(),
408
            scoll::INDICES_METHOD
409
                .clone()
410
                .with_concrete_types(&[(STypeVar::t(), SType::SLong)].iter().cloned().collect()),
411
            vec![],
412
        )
413
        .unwrap()
414
        .into();
415
        let expr: Expr = MethodCall::new(
416
            coll_const.into(),
417
            scoll::FLATMAP_METHOD.clone().with_concrete_types(
418
                &[
419
                    (STypeVar::iv(), SType::SColl(Box::new(SType::SLong))),
420
                    (STypeVar::ov(), SType::SInt),
421
                ]
422
                .iter()
423
                .cloned()
424
                .collect(),
425
            ),
426
            vec![FuncValue::new(
427
                vec![FuncArg {
428
                    idx: 1.into(),
429
                    tpe: SType::SColl(Box::new(SType::SLong)),
430
                }],
431
                body,
432
            )
433
            .into()],
434
        )
435
        .unwrap()
436
        .into();
437
        let res = eval_out_wo_ctx::<Vec<i32>>(&expr);
438
        assert_eq!(res, vec![0, 1, 0]);
439
    }
440

441
    #[test]
442
    fn eval_zip_empty() {
443
        // Both empty
444
        let empty_coll_const: Constant = Vec::<i64>::new().into();
445
        let empty_input: Constant = Vec::<bool>::new().into();
446
        let expr: Expr = MethodCall::new(
447
            empty_coll_const.into(),
448
            scoll::ZIP_METHOD.clone().with_concrete_types(
449
                &[
450
                    (STypeVar::t(), SType::SLong),
451
                    (STypeVar::iv(), SType::SBoolean),
452
                ]
453
                .iter()
454
                .cloned()
455
                .collect(),
456
            ),
457
            vec![empty_input.into()],
458
        )
459
        .unwrap()
460
        .into();
461
        let res = eval_out_wo_ctx::<Vec<(i64, bool)>>(&expr);
462
        assert_eq!(res, Vec::<(i64, bool)>::new());
463

464
        // Only obj empty
465
        let empty_coll_const: Constant = Vec::<i64>::new().into();
466
        let input: Constant = vec![true, false].into();
467
        let expr: Expr = MethodCall::new(
468
            empty_coll_const.into(),
469
            scoll::ZIP_METHOD.clone().with_concrete_types(
470
                &[
471
                    (STypeVar::t(), SType::SLong),
472
                    (STypeVar::iv(), SType::SBoolean),
473
                ]
474
                .iter()
475
                .cloned()
476
                .collect(),
477
            ),
478
            vec![input.into()],
479
        )
480
        .unwrap()
481
        .into();
482
        let res = eval_out_wo_ctx::<Vec<(i64, bool)>>(&expr);
483
        assert_eq!(res, Vec::<(i64, bool)>::new());
484

485
        // Only input empty
486
        let coll_const: Constant = vec![1i64, 2i64].into();
487
        let empty_input: Constant = Vec::<bool>::new().into();
488
        let expr: Expr = MethodCall::new(
489
            coll_const.into(),
490
            scoll::ZIP_METHOD.clone().with_concrete_types(
491
                &[
492
                    (STypeVar::t(), SType::SLong),
493
                    (STypeVar::iv(), SType::SBoolean),
494
                ]
495
                .iter()
496
                .cloned()
497
                .collect(),
498
            ),
499
            vec![empty_input.into()],
500
        )
501
        .unwrap()
502
        .into();
503
        let res = eval_out_wo_ctx::<Vec<(i64, bool)>>(&expr);
504
        assert_eq!(res, Vec::<(i64, bool)>::new());
505
    }
506

507
    #[test]
508
    fn eval_zip_same_length() {
509
        let coll_const: Constant = vec![1i64, 2i64].into();
510
        let input: Constant = vec![true, false].into();
511
        let expr: Expr = MethodCall::new(
512
            coll_const.into(),
513
            scoll::ZIP_METHOD.clone().with_concrete_types(
514
                &[
515
                    (STypeVar::t(), SType::SLong),
516
                    (STypeVar::iv(), SType::SBoolean),
517
                ]
518
                .iter()
519
                .cloned()
520
                .collect(),
521
            ),
522
            vec![input.into()],
523
        )
524
        .unwrap()
525
        .into();
526
        let res = eval_out_wo_ctx::<Vec<(i64, bool)>>(&expr);
527
        assert_eq!(res, vec![(1i64, true), (2, false)]);
528
    }
529

530
    #[test]
531
    fn eval_zip_different_lengths() {
532
        // Input shorter than obj
533
        let coll_const: Constant = vec![1i64, 2i64].into();
534
        let short_input: Constant = vec![true].into();
535
        let expr: Expr = MethodCall::new(
536
            coll_const.into(),
537
            scoll::ZIP_METHOD.clone().with_concrete_types(
538
                &[
539
                    (STypeVar::t(), SType::SLong),
540
                    (STypeVar::iv(), SType::SBoolean),
541
                ]
542
                .iter()
543
                .cloned()
544
                .collect(),
545
            ),
546
            vec![short_input.into()],
547
        )
548
        .unwrap()
549
        .into();
550
        let res = eval_out_wo_ctx::<Vec<(i64, bool)>>(&expr);
551
        assert_eq!(res, vec![(1i64, true)]);
552

553
        // Input longer than obj
554
        let coll_const: Constant = vec![1i64, 2i64].into();
555
        let long_input: Constant = vec![true, false, true].into();
556
        let expr: Expr = MethodCall::new(
557
            coll_const.into(),
558
            scoll::ZIP_METHOD.clone().with_concrete_types(
559
                &[
560
                    (STypeVar::t(), SType::SLong),
561
                    (STypeVar::iv(), SType::SBoolean),
562
                ]
563
                .iter()
564
                .cloned()
565
                .collect(),
566
            ),
567
            vec![long_input.into()],
568
        )
569
        .unwrap()
570
        .into();
571
        let res = eval_out_wo_ctx::<Vec<(i64, bool)>>(&expr);
572
        assert_eq!(res, vec![(1i64, true), (2, false)]);
573
    }
574

575
    #[test]
576
    fn eval_indices() {
577
        let coll_const: Constant = vec![1i64, 2i64, 3i64].into();
578
        let expr: Expr = MethodCall::new(
579
            coll_const.into(),
580
            scoll::INDICES_METHOD
581
                .clone()
582
                .with_concrete_types(&[(STypeVar::t(), SType::SLong)].iter().cloned().collect()),
583
            vec![],
584
        )
585
        .unwrap()
586
        .into();
587
        let res = eval_out_wo_ctx::<Vec<i32>>(&expr);
588
        assert_eq!(res, vec![0i32, 1i32, 2i32]);
589
    }
590

591
    #[test]
592
    fn eval_indices_empty_coll() {
593
        let coll_const: Constant = Vec::<i64>::new().into();
594
        let expr: Expr = MethodCall::new(
595
            coll_const.into(),
596
            scoll::INDICES_METHOD
597
                .clone()
598
                .with_concrete_types(&[(STypeVar::t(), SType::SLong)].iter().cloned().collect()),
599
            vec![],
600
        )
601
        .unwrap()
602
        .into();
603
        let res = eval_out_wo_ctx::<Vec<i32>>(&expr);
604
        assert_eq!(res, Vec::<i32>::new());
605
    }
606

607
    #[test]
608
    fn eval_patch() {
609
        let coll_const: Constant = vec![1i64, 5i64, 5i64].into();
610
        let patch_input: Vec<i64> = vec![2i64, 3i64];
611

612
        let expr: Expr = MethodCall::new(
613
            coll_const.into(),
614
            scoll::PATCH_METHOD
615
                .clone()
616
                .with_concrete_types(&[(STypeVar::t(), SType::SLong)].iter().cloned().collect()),
617
            vec![1i32.into(), patch_input.into(), 2i32.into()],
618
        )
619
        .unwrap()
620
        .into();
621
        let res = eval_out_wo_ctx::<Vec<i64>>(&expr);
622
        assert_eq!(res, vec![1i64, 2i64, 3i64]);
623
    }
624

625
    #[test]
626
    fn eval_patch_addition() {
627
        let coll_const: Constant = vec![1i64, 2i64, 4i64, 5i64].into();
628
        let patch_input: Vec<i64> = vec![3i64];
629

630
        let expr: Expr = MethodCall::new(
631
            coll_const.into(),
632
            scoll::PATCH_METHOD
633
                .clone()
634
                .with_concrete_types(&[(STypeVar::t(), SType::SLong)].iter().cloned().collect()),
635
            vec![2i32.into(), patch_input.into(), 0i32.into()],
636
        )
637
        .unwrap()
638
        .into();
639
        let res = eval_out_wo_ctx::<Vec<i64>>(&expr);
640
        assert_eq!(res, vec![1i64, 2i64, 3i64, 4i64, 5i64]);
641
    }
642

643
    #[test]
644
    fn eval_patch_subtraction() {
645
        let coll_const: Constant = vec![1i64, 2i64, 5i64, 5i64, 4i64, 5i64].into();
646
        let patch_input: Vec<i64> = vec![3i64];
647

648
        let expr: Expr = MethodCall::new(
649
            coll_const.into(),
650
            scoll::PATCH_METHOD
651
                .clone()
652
                .with_concrete_types(&[(STypeVar::t(), SType::SLong)].iter().cloned().collect()),
653
            vec![2i32.into(), patch_input.into(), 2i32.into()],
654
        )
655
        .unwrap()
656
        .into();
657
        let res = eval_out_wo_ctx::<Vec<i64>>(&expr);
658
        assert_eq!(res, vec![1i64, 2i64, 3i64, 4i64, 5i64]);
659
    }
660

661
    #[test]
662
    fn eval_patch_index_oob() {
663
        let coll_const: Constant = vec![1i64, 2i64, 3i64].into();
664
        let patch_input: Vec<i64> = vec![4i64, 5i64];
665

666
        let expr: Expr = MethodCall::new(
667
            coll_const.into(),
668
            scoll::PATCH_METHOD
669
                .clone()
670
                .with_concrete_types(&[(STypeVar::t(), SType::SLong)].iter().cloned().collect()),
671
            vec![9i32.into(), patch_input.into(), 9i32.into()],
672
        )
673
        .unwrap()
674
        .into();
675
        let res = eval_out_wo_ctx::<Vec<i64>>(&expr);
676
        assert_eq!(res, vec![1i64, 2i64, 3i64, 4i64, 5i64]);
677
    }
678

679
    #[test]
680
    fn eval_patch_index_negative() {
681
        let coll_const: Constant = vec![1i64, 2i64, 3i64].into();
682
        let patch_input: Vec<i64> = vec![4i64, 5i64];
683

684
        let expr: Expr = MethodCall::new(
685
            coll_const.into(),
686
            scoll::PATCH_METHOD
687
                .clone()
688
                .with_concrete_types(&[(STypeVar::t(), SType::SLong)].iter().cloned().collect()),
689
            vec![(-1i32).into(), patch_input.into(), (-1i32).into()],
690
        )
691
        .unwrap()
692
        .into();
693
        let res = eval_out_wo_ctx::<Vec<i64>>(&expr);
694
        assert_eq!(res, vec![4i64, 5i64, 1i64, 2i64, 3i64]);
695
    }
696

697
    #[test]
698
    fn eval_update() {
699
        let coll_const: Constant = vec![1i64, 2i64, 3i64].into();
700
        let expr: Expr = MethodCall::new(
701
            coll_const.into(),
702
            scoll::UPDATED_METHOD
703
                .clone()
704
                .with_concrete_types(&[(STypeVar::t(), SType::SLong)].iter().cloned().collect()),
705
            vec![1i32.into(), 5i64.into()],
706
        )
707
        .unwrap()
708
        .into();
709
        let res = eval_out_wo_ctx::<Vec<i64>>(&expr);
710
        assert_eq!(res, vec![1i64, 5i64, 3i64]);
711
    }
712

713
    #[test]
714
    fn eval_update_oob() {
715
        let coll_const: Constant = vec![1i64, 2i64, 3i64].into();
716
        let expr: Expr = MethodCall::new(
717
            coll_const.into(),
718
            scoll::UPDATED_METHOD
719
                .clone()
720
                .with_concrete_types(&[(STypeVar::t(), SType::SLong)].iter().cloned().collect()),
721
            vec![5i32.into(), 5i64.into()],
722
        )
723
        .unwrap()
724
        .into();
725
        assert!(try_eval_out_wo_ctx::<Vec<i64>>(&expr).is_err());
726
    }
727

728
    #[test]
729
    fn eval_update_many() {
730
        let coll_const: Constant = vec![1i64, 2i64, 3i64].into();
731

732
        let indexes_input: Vec<i32> = vec![1i32, 2i32];
733
        let updates_input: Vec<i64> = vec![5i64, 6i64];
734
        let expr: Expr = MethodCall::new(
735
            coll_const.into(),
736
            scoll::UPDATE_MANY_METHOD
737
                .clone()
738
                .with_concrete_types(&[(STypeVar::t(), SType::SLong)].iter().cloned().collect()),
739
            vec![indexes_input.into(), updates_input.into()],
740
        )
741
        .unwrap()
742
        .into();
743
        let res = eval_out_wo_ctx::<Vec<i64>>(&expr);
744
        assert_eq!(res, vec![1i64, 5i64, 6i64]);
745
    }
746

747
    #[test]
748
    fn eval_update_many_index_oob() {
749
        let coll_const: Constant = vec![1i64, 2i64, 3i64].into();
750

751
        let indexes_input: Vec<i32> = vec![1i32, 5i32];
752
        let updates_input: Vec<i64> = vec![5i64, 6i64];
753
        let expr: Expr = MethodCall::new(
754
            coll_const.into(),
755
            scoll::UPDATE_MANY_METHOD
756
                .clone()
757
                .with_concrete_types(&[(STypeVar::t(), SType::SLong)].iter().cloned().collect()),
758
            vec![indexes_input.into(), updates_input.into()],
759
        )
760
        .unwrap()
761
        .into();
762
        assert!(try_eval_out_wo_ctx::<Vec<i64>>(&expr).is_err());
763
    }
764

765
    #[test]
766
    fn eval_update_many_len_mismatch() {
767
        let coll_const: Constant = vec![1i64, 2i64, 3i64].into();
768

769
        let indexes_input: Vec<i32> = vec![1i32];
770
        let updates_input: Vec<i64> = vec![5i64, 6i64];
771
        let expr: Expr = MethodCall::new(
772
            coll_const.into(),
773
            scoll::UPDATE_MANY_METHOD
774
                .clone()
775
                .with_concrete_types(&[(STypeVar::t(), SType::SLong)].iter().cloned().collect()),
776
            vec![indexes_input.into(), updates_input.into()],
777
        )
778
        .unwrap()
779
        .into();
780
        assert!(try_eval_out_wo_ctx::<Vec<i64>>(&expr).is_err());
781
    }
782
}
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