|
15 | 15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 |
|
17 | 17 | use clarity_types::types::MAX_TO_ASCII_BUFFER_LEN; |
| 18 | +use proptest::prelude::*; |
18 | 19 | use stacks_common::types::StacksEpochId; |
19 | 20 |
|
20 | 21 | pub use crate::vm::analysis::errors::CheckErrors; |
| 22 | +use crate::vm::tests::proptest_utils::{ |
| 23 | + contract_name_strategy, execute_versioned, standard_principal_strategy, |
| 24 | + to_ascii_buffer_snippet_strategy, utf8_string_ascii_only_snippet_strategy, |
| 25 | + utf8_string_snippet_strategy, |
| 26 | +}; |
21 | 27 | use crate::vm::tests::test_clarity_versions; |
22 | 28 | use crate::vm::types::SequenceSubtype::BufferType; |
23 | 29 | use crate::vm::types::TypeSignature::SequenceType; |
@@ -585,7 +591,147 @@ fn test_to_ascii(version: ClarityVersion, epoch: StacksEpochId) { |
585 | 591 | "(to-ascii? 0x{})", |
586 | 592 | "ff".repeat(MAX_TO_ASCII_BUFFER_LEN as usize + 1) |
587 | 593 | ); |
588 | | - let result = execute_with_parameters(response_to_ascii, version, epoch, false); |
| 594 | + let result = execute_with_parameters(&oversized_buffer_to_ascii, version, epoch, false); |
589 | 595 | // This should fail at analysis time since the value is too big |
590 | 596 | assert!(result.is_err()); |
591 | 597 | } |
| 598 | + |
| 599 | +fn evaluate_to_ascii(snippet: &str) -> Value { |
| 600 | + execute_versioned(snippet, ClarityVersion::Clarity4) |
| 601 | + .unwrap_or_else(|e| panic!("Execution failed for snippet `{snippet}`: {e:?}")) |
| 602 | + .unwrap_or_else(|| panic!("Execution returned no value for snippet `{snippet}`")) |
| 603 | +} |
| 604 | + |
| 605 | +proptest! { |
| 606 | + #[test] |
| 607 | + fn prop_to_ascii_from_ints(int_value in any::<i128>()) { |
| 608 | + let snippet = format!("(to-ascii? {int_value})"); |
| 609 | + let evaluation = evaluate_to_ascii(&snippet); |
| 610 | + |
| 611 | + let expected_inner = Value::string_ascii_from_bytes(int_value.to_string().into_bytes()) |
| 612 | + .expect("int string should be valid ASCII"); |
| 613 | + let expected = Value::okay(expected_inner).expect("response wrapping should succeed"); |
| 614 | + |
| 615 | + prop_assert_eq!(expected, evaluation); |
| 616 | + } |
| 617 | + |
| 618 | + #[test] |
| 619 | + fn prop_to_ascii_from_uints(uint_value in any::<u128>()) { |
| 620 | + let snippet = format!("(to-ascii? u{uint_value})"); |
| 621 | + let evaluation = evaluate_to_ascii(&snippet); |
| 622 | + |
| 623 | + let expected_inner = Value::string_ascii_from_bytes(format!("u{uint_value}").into_bytes()) |
| 624 | + .expect("uint string should be valid ASCII"); |
| 625 | + let expected = Value::okay(expected_inner).expect("response wrapping should succeed"); |
| 626 | + |
| 627 | + prop_assert_eq!(expected, evaluation); |
| 628 | + } |
| 629 | + |
| 630 | + #[test] |
| 631 | + fn prop_to_ascii_from_bools(bool_value in any::<bool>()) { |
| 632 | + let literal = if bool_value { "true" } else { "false" }; |
| 633 | + let snippet = format!("(to-ascii? {literal})"); |
| 634 | + let evaluation = evaluate_to_ascii(&snippet); |
| 635 | + |
| 636 | + let expected_inner = Value::string_ascii_from_bytes(literal.as_bytes().to_vec()) |
| 637 | + .expect("bool string should be valid ASCII"); |
| 638 | + let expected = Value::okay(expected_inner).expect("response wrapping should succeed"); |
| 639 | + |
| 640 | + prop_assert_eq!(expected, evaluation); |
| 641 | + } |
| 642 | + |
| 643 | + #[test] |
| 644 | + fn prop_to_ascii_from_standard_principals(principal in standard_principal_strategy()) { |
| 645 | + let literal = format!("'{}", principal); |
| 646 | + let snippet = format!("(to-ascii? {literal})"); |
| 647 | + let evaluation = evaluate_to_ascii(&snippet); |
| 648 | + |
| 649 | + let expected_inner = Value::string_ascii_from_bytes(principal.to_string().into_bytes()) |
| 650 | + .expect("principal string should be valid ASCII"); |
| 651 | + let expected = Value::okay(expected_inner).expect("response wrapping should succeed"); |
| 652 | + |
| 653 | + prop_assert_eq!(expected, evaluation); |
| 654 | + } |
| 655 | + |
| 656 | + #[test] |
| 657 | + fn prop_to_ascii_from_contract_principals( |
| 658 | + issuer in standard_principal_strategy(), |
| 659 | + contract_name in contract_name_strategy(), |
| 660 | + ) { |
| 661 | + let contract_name_str = contract_name.to_string(); |
| 662 | + let literal = format!("'{}.{}", issuer, contract_name_str); |
| 663 | + let snippet = format!("(to-ascii? {literal})"); |
| 664 | + let evaluation = evaluate_to_ascii(&snippet); |
| 665 | + |
| 666 | + let expected_inner = Value::string_ascii_from_bytes( |
| 667 | + format!("{}.{}", issuer, contract_name_str).into_bytes() |
| 668 | + ) |
| 669 | + .expect("contract principal string should be valid ASCII"); |
| 670 | + let expected = Value::okay(expected_inner).expect("response wrapping should succeed"); |
| 671 | + |
| 672 | + prop_assert_eq!(expected, evaluation); |
| 673 | + } |
| 674 | + |
| 675 | + #[test] |
| 676 | + fn prop_to_ascii_from_buffers(buffer in to_ascii_buffer_snippet_strategy()) { |
| 677 | + let snippet = format!("(to-ascii? {buffer})"); |
| 678 | + let evaluation = evaluate_to_ascii(&snippet); |
| 679 | + |
| 680 | + let expected_inner = Value::string_ascii_from_bytes(buffer.to_string().into_bytes()) |
| 681 | + .expect("buffer string should be valid ASCII"); |
| 682 | + let expected = Value::okay(expected_inner).expect("response wrapping should succeed"); |
| 683 | + |
| 684 | + prop_assert_eq!(expected, evaluation); |
| 685 | + } |
| 686 | + |
| 687 | + #[test] |
| 688 | + fn prop_to_ascii_from_ascii_utf8_strings(utf8_string in utf8_string_ascii_only_snippet_strategy()) { |
| 689 | + let snippet = format!("(to-ascii? {utf8_string})"); |
| 690 | + let evaluation = evaluate_to_ascii(&snippet); |
| 691 | + |
| 692 | + let ascii_snippet = &utf8_string[1..]; // Remove the u prefix |
| 693 | + let expected_inner = execute_versioned(ascii_snippet, ClarityVersion::Clarity4) |
| 694 | + .unwrap_or_else(|e| panic!("Execution failed for `{ascii_snippet}`: {e:?}")) |
| 695 | + .unwrap_or_else(|| panic!("Execution returned no value for `{ascii_snippet}`")); |
| 696 | + let expected = Value::okay(expected_inner).expect("response wrapping should succeed"); |
| 697 | + |
| 698 | + prop_assert_eq!(expected, evaluation); |
| 699 | + } |
| 700 | + |
| 701 | + #[test] |
| 702 | + fn prop_to_ascii_from_utf8_strings(utf8_string in utf8_string_snippet_strategy()) { |
| 703 | + let snippet = format!("(to-ascii? {utf8_string})"); |
| 704 | + let evaluation = evaluate_to_ascii(&snippet); |
| 705 | + |
| 706 | + let literal_value = execute_versioned(&utf8_string, ClarityVersion::Clarity4) |
| 707 | + .unwrap_or_else(|e| panic!("Execution failed for literal `{utf8_string}`: {e:?}")) |
| 708 | + .unwrap_or_else(|| panic!("Execution returned no value for literal `{utf8_string}`")); |
| 709 | + |
| 710 | + let utf8_chars = match &literal_value { |
| 711 | + Value::Sequence(SequenceData::String(CharType::UTF8(data))) => data.data.clone(), |
| 712 | + _ => panic!("Expected UTF-8 string literal, got `{literal_value:?}`"), |
| 713 | + }; |
| 714 | + let is_ascii = utf8_chars |
| 715 | + .iter() |
| 716 | + .all(|char_bytes| char_bytes.len() == 1 && char_bytes[0].is_ascii()); |
| 717 | + |
| 718 | + if is_ascii { |
| 719 | + let ascii_bytes: Vec<u8> = utf8_chars |
| 720 | + .iter() |
| 721 | + .map(|char_bytes| char_bytes[0]) |
| 722 | + .collect(); |
| 723 | + match Value::string_ascii_from_bytes(ascii_bytes) { |
| 724 | + Ok(expected_inner) => { |
| 725 | + let expected = Value::okay(expected_inner) |
| 726 | + .expect("response wrapping should succeed"); |
| 727 | + prop_assert_eq!(expected, evaluation); |
| 728 | + } |
| 729 | + Err(_) => { |
| 730 | + prop_assert_eq!(Value::err_uint(1), evaluation); |
| 731 | + } |
| 732 | + } |
| 733 | + } else { |
| 734 | + prop_assert_eq!(Value::err_uint(1), evaluation); |
| 735 | + } |
| 736 | + } |
| 737 | +} |
0 commit comments