Skip to content

Commit 0a1c1c0

Browse files
Add test vector for that uses traits and associated types in a contract (#1598)
### What Add test vector for trait associated types in contracts, demonstrating the pattern where a trait uses associated types to delegate to default implementations. ### Why The OpenZeppelin contract library uses this pattern, and having test vector that uses the pattern provides some visibility to how the generated code expands alongside associated types. For #1544
1 parent 71a1182 commit 0a1c1c0

File tree

6 files changed

+652
-0
lines changed

6 files changed

+652
-0
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 375 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,375 @@
1+
#![feature(prelude_import)]
2+
#![no_std]
3+
#[macro_use]
4+
extern crate core;
5+
#[prelude_import]
6+
use core::prelude::rust_2021::*;
7+
use soroban_sdk::{contract, contractimpl, Env, String};
8+
pub struct DefaultImpl;
9+
impl Trait for DefaultImpl {
10+
type Impl = Self;
11+
fn exec(env: &Env) -> String {
12+
String::from_str(env, "default")
13+
}
14+
}
15+
pub trait Trait {
16+
type Impl: Trait;
17+
fn exec(env: &Env) -> String {
18+
Self::Impl::exec(env)
19+
}
20+
}
21+
pub struct Contract;
22+
///ContractArgs is a type for building arg lists for functions defined in "Contract".
23+
pub struct ContractArgs;
24+
///ContractClient is a client for calling the contract defined in "Contract".
25+
pub struct ContractClient<'a> {
26+
pub env: soroban_sdk::Env,
27+
pub address: soroban_sdk::Address,
28+
#[doc(hidden)]
29+
set_auths: Option<&'a [soroban_sdk::xdr::SorobanAuthorizationEntry]>,
30+
#[doc(hidden)]
31+
mock_auths: Option<&'a [soroban_sdk::testutils::MockAuth<'a>]>,
32+
#[doc(hidden)]
33+
mock_all_auths: bool,
34+
#[doc(hidden)]
35+
allow_non_root_auth: bool,
36+
}
37+
impl<'a> ContractClient<'a> {
38+
pub fn new(env: &soroban_sdk::Env, address: &soroban_sdk::Address) -> Self {
39+
Self {
40+
env: env.clone(),
41+
address: address.clone(),
42+
set_auths: None,
43+
mock_auths: None,
44+
mock_all_auths: false,
45+
allow_non_root_auth: false,
46+
}
47+
}
48+
/// Set authorizations in the environment which will be consumed by
49+
/// contracts when they invoke `Address::require_auth` or
50+
/// `Address::require_auth_for_args` functions.
51+
///
52+
/// Requires valid signatures for the authorization to be successful.
53+
/// To mock auth without requiring valid signatures, use `mock_auths`.
54+
///
55+
/// See `soroban_sdk::Env::set_auths` for more details and examples.
56+
pub fn set_auths(&self, auths: &'a [soroban_sdk::xdr::SorobanAuthorizationEntry]) -> Self {
57+
Self {
58+
env: self.env.clone(),
59+
address: self.address.clone(),
60+
set_auths: Some(auths),
61+
mock_auths: self.mock_auths.clone(),
62+
mock_all_auths: false,
63+
allow_non_root_auth: false,
64+
}
65+
}
66+
/// Mock authorizations in the environment which will cause matching invokes
67+
/// of `Address::require_auth` and `Address::require_auth_for_args` to
68+
/// pass.
69+
///
70+
/// See `soroban_sdk::Env::set_auths` for more details and examples.
71+
pub fn mock_auths(&self, mock_auths: &'a [soroban_sdk::testutils::MockAuth<'a>]) -> Self {
72+
Self {
73+
env: self.env.clone(),
74+
address: self.address.clone(),
75+
set_auths: self.set_auths.clone(),
76+
mock_auths: Some(mock_auths),
77+
mock_all_auths: false,
78+
allow_non_root_auth: false,
79+
}
80+
}
81+
/// Mock all calls to the `Address::require_auth` and
82+
/// `Address::require_auth_for_args` functions in invoked contracts,
83+
/// having them succeed as if authorization was provided.
84+
///
85+
/// See `soroban_sdk::Env::mock_all_auths` for more details and
86+
/// examples.
87+
pub fn mock_all_auths(&self) -> Self {
88+
Self {
89+
env: self.env.clone(),
90+
address: self.address.clone(),
91+
set_auths: None,
92+
mock_auths: None,
93+
mock_all_auths: true,
94+
allow_non_root_auth: false,
95+
}
96+
}
97+
/// A version of `mock_all_auths` that allows authorizations that
98+
/// are not present in the root invocation.
99+
///
100+
/// Refer to `mock_all_auths` documentation for details and
101+
/// prefer using `mock_all_auths` unless non-root authorization is
102+
/// required.
103+
///
104+
/// See `soroban_sdk::Env::mock_all_auths_allowing_non_root_auth`
105+
/// for more details and examples.
106+
pub fn mock_all_auths_allowing_non_root_auth(&self) -> Self {
107+
Self {
108+
env: self.env.clone(),
109+
address: self.address.clone(),
110+
set_auths: None,
111+
mock_auths: None,
112+
mock_all_auths: true,
113+
allow_non_root_auth: true,
114+
}
115+
}
116+
}
117+
mod __contract_fn_set_registry {
118+
use super::*;
119+
extern crate std;
120+
use std::collections::BTreeMap;
121+
use std::sync::Mutex;
122+
pub type F = soroban_sdk::testutils::ContractFunctionF;
123+
static FUNCS: Mutex<BTreeMap<&'static str, &'static F>> = Mutex::new(BTreeMap::new());
124+
pub fn register(name: &'static str, func: &'static F) {
125+
FUNCS.lock().unwrap().insert(name, func);
126+
}
127+
pub fn call(
128+
name: &str,
129+
env: soroban_sdk::Env,
130+
args: &[soroban_sdk::Val],
131+
) -> Option<soroban_sdk::Val> {
132+
let fopt: Option<&'static F> = FUNCS.lock().unwrap().get(name).map(|f| f.clone());
133+
fopt.map(|f| f(env, args))
134+
}
135+
}
136+
impl soroban_sdk::testutils::ContractFunctionRegister for Contract {
137+
fn register(name: &'static str, func: &'static __contract_fn_set_registry::F) {
138+
__contract_fn_set_registry::register(name, func);
139+
}
140+
}
141+
#[doc(hidden)]
142+
impl soroban_sdk::testutils::ContractFunctionSet for Contract {
143+
fn call(
144+
&self,
145+
func: &str,
146+
env: soroban_sdk::Env,
147+
args: &[soroban_sdk::Val],
148+
) -> Option<soroban_sdk::Val> {
149+
__contract_fn_set_registry::call(func, env, args)
150+
}
151+
}
152+
impl Trait for Contract {
153+
type Impl = DefaultImpl;
154+
fn exec(env: &Env) -> String {
155+
Self::Impl::exec(env)
156+
}
157+
}
158+
#[doc(hidden)]
159+
#[allow(non_snake_case)]
160+
pub mod __Contract__exec__spec {
161+
#[doc(hidden)]
162+
#[allow(non_snake_case)]
163+
#[allow(non_upper_case_globals)]
164+
pub static __SPEC_XDR_FN_EXEC: [u8; 28usize] = super::Contract::spec_xdr_exec();
165+
}
166+
impl Contract {
167+
#[allow(non_snake_case)]
168+
pub const fn spec_xdr_exec() -> [u8; 28usize] {
169+
*b"\0\0\0\0\0\0\0\0\0\0\0\x04exec\0\0\0\0\0\0\0\x01\0\0\0\x10"
170+
}
171+
}
172+
impl<'a> ContractClient<'a> {
173+
pub fn exec(&self) -> String {
174+
use core::ops::Not;
175+
let old_auth_manager = self
176+
.env
177+
.in_contract()
178+
.not()
179+
.then(|| self.env.host().snapshot_auth_manager().unwrap());
180+
{
181+
if let Some(set_auths) = self.set_auths {
182+
self.env.set_auths(set_auths);
183+
}
184+
if let Some(mock_auths) = self.mock_auths {
185+
self.env.mock_auths(mock_auths);
186+
}
187+
if self.mock_all_auths {
188+
if self.allow_non_root_auth {
189+
self.env.mock_all_auths_allowing_non_root_auth();
190+
} else {
191+
self.env.mock_all_auths();
192+
}
193+
}
194+
}
195+
use soroban_sdk::{FromVal, IntoVal};
196+
let res = self.env.invoke_contract(
197+
&self.address,
198+
&{
199+
#[allow(deprecated)]
200+
const SYMBOL: soroban_sdk::Symbol = soroban_sdk::Symbol::short("exec");
201+
SYMBOL
202+
},
203+
::soroban_sdk::Vec::new(&self.env),
204+
);
205+
if let Some(old_auth_manager) = old_auth_manager {
206+
self.env.host().set_auth_manager(old_auth_manager).unwrap();
207+
}
208+
res
209+
}
210+
pub fn try_exec(
211+
&self,
212+
) -> Result<
213+
Result<
214+
String,
215+
<String as soroban_sdk::TryFromVal<soroban_sdk::Env, soroban_sdk::Val>>::Error,
216+
>,
217+
Result<soroban_sdk::Error, soroban_sdk::InvokeError>,
218+
> {
219+
use core::ops::Not;
220+
let old_auth_manager = self
221+
.env
222+
.in_contract()
223+
.not()
224+
.then(|| self.env.host().snapshot_auth_manager().unwrap());
225+
{
226+
if let Some(set_auths) = self.set_auths {
227+
self.env.set_auths(set_auths);
228+
}
229+
if let Some(mock_auths) = self.mock_auths {
230+
self.env.mock_auths(mock_auths);
231+
}
232+
if self.mock_all_auths {
233+
self.env.mock_all_auths();
234+
}
235+
}
236+
use soroban_sdk::{FromVal, IntoVal};
237+
let res = self.env.try_invoke_contract(
238+
&self.address,
239+
&{
240+
#[allow(deprecated)]
241+
const SYMBOL: soroban_sdk::Symbol = soroban_sdk::Symbol::short("exec");
242+
SYMBOL
243+
},
244+
::soroban_sdk::Vec::new(&self.env),
245+
);
246+
if let Some(old_auth_manager) = old_auth_manager {
247+
self.env.host().set_auth_manager(old_auth_manager).unwrap();
248+
}
249+
res
250+
}
251+
}
252+
impl ContractArgs {
253+
#[inline(always)]
254+
#[allow(clippy::unused_unit)]
255+
pub fn exec<'i>() -> () {
256+
()
257+
}
258+
}
259+
#[doc(hidden)]
260+
#[allow(non_snake_case)]
261+
pub mod __Contract__exec {
262+
use super::*;
263+
#[deprecated(note = "use `ContractClient::new(&env, &contract_id).exec` instead")]
264+
pub fn invoke_raw(env: soroban_sdk::Env) -> soroban_sdk::Val {
265+
use super::Trait;
266+
<_ as soroban_sdk::IntoVal<soroban_sdk::Env, soroban_sdk::Val>>::into_val(
267+
#[allow(deprecated)]
268+
&<super::Contract>::exec(&env),
269+
&env,
270+
)
271+
}
272+
#[deprecated(note = "use `ContractClient::new(&env, &contract_id).exec` instead")]
273+
pub fn invoke_raw_slice(env: soroban_sdk::Env, args: &[soroban_sdk::Val]) -> soroban_sdk::Val {
274+
if args.len() != 0usize {
275+
{
276+
::core::panicking::panic_fmt(format_args!(
277+
"invalid number of input arguments: {0} expected, got {1}",
278+
0usize,
279+
args.len(),
280+
));
281+
};
282+
}
283+
#[allow(deprecated)]
284+
invoke_raw(env)
285+
}
286+
#[deprecated(note = "use `ContractClient::new(&env, &contract_id).exec` instead")]
287+
pub extern "C" fn invoke_raw_extern() -> soroban_sdk::Val {
288+
#[allow(deprecated)]
289+
invoke_raw(soroban_sdk::Env::default())
290+
}
291+
use super::*;
292+
}
293+
#[doc(hidden)]
294+
#[allow(non_snake_case)]
295+
#[allow(unused)]
296+
fn __Contract_Trait_2706c619fe73f0cf112473c6ee02e66c04e1c01c110b0c37b88d8eb509630c9f_ctor() {
297+
#[allow(unsafe_code)]
298+
{
299+
#[link_section = ".init_array"]
300+
#[used]
301+
#[allow(non_upper_case_globals, non_snake_case)]
302+
#[doc(hidden)]
303+
static f: extern "C" fn() -> ::ctor::__support::CtorRetType = {
304+
#[link_section = ".text.startup"]
305+
#[allow(non_snake_case)]
306+
extern "C" fn f() -> ::ctor::__support::CtorRetType {
307+
unsafe {
308+
__Contract_Trait_2706c619fe73f0cf112473c6ee02e66c04e1c01c110b0c37b88d8eb509630c9f_ctor();
309+
};
310+
core::default::Default::default()
311+
}
312+
f
313+
};
314+
}
315+
{
316+
<Contract as soroban_sdk::testutils::ContractFunctionRegister>::register(
317+
"exec",
318+
#[allow(deprecated)]
319+
&__Contract__exec::invoke_raw_slice,
320+
);
321+
}
322+
}
323+
mod test {
324+
use crate::{Contract, ContractClient};
325+
use soroban_sdk::{Env, String};
326+
extern crate test;
327+
#[rustc_test_marker = "test::test_exec"]
328+
#[doc(hidden)]
329+
pub const test_exec: test::TestDescAndFn = test::TestDescAndFn {
330+
desc: test::TestDesc {
331+
name: test::StaticTestName("test::test_exec"),
332+
ignore: false,
333+
ignore_message: ::core::option::Option::None,
334+
source_file: "tests/associated_type/src/lib.rs",
335+
start_line: 42usize,
336+
start_col: 8usize,
337+
end_line: 42usize,
338+
end_col: 17usize,
339+
compile_fail: false,
340+
no_run: false,
341+
should_panic: test::ShouldPanic::No,
342+
test_type: test::TestType::UnitTest,
343+
},
344+
testfn: test::StaticTestFn(
345+
#[coverage(off)]
346+
|| test::assert_test_result(test_exec()),
347+
),
348+
};
349+
fn test_exec() {
350+
let e = Env::default();
351+
let contract_id = e.register(Contract, ());
352+
let client = ContractClient::new(&e, &contract_id);
353+
let res = client.exec();
354+
match (&res, &String::from_str(&e, "default")) {
355+
(left_val, right_val) => {
356+
if !(*left_val == *right_val) {
357+
let kind = ::core::panicking::AssertKind::Eq;
358+
::core::panicking::assert_failed(
359+
kind,
360+
&*left_val,
361+
&*right_val,
362+
::core::option::Option::None,
363+
);
364+
}
365+
}
366+
};
367+
}
368+
}
369+
#[rustc_main]
370+
#[coverage(off)]
371+
#[doc(hidden)]
372+
pub fn main() -> () {
373+
extern crate test;
374+
test::test_main_static(&[&test_exec])
375+
}

0 commit comments

Comments
 (0)