1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! `Events::all()` return type changed from `Vec<(Address, Vec<Val>, Val)>` to [`ContractEvents`].
//!
//! The [`ContractEvents`] struct provides a more ergonomic interface for asserting on
//! emitted events. It can be compared directly with:
//! - `[xdr::ContractEvent; _]`
//! - `std::vec::Vec<xdr::ContractEvent>`
//! - `Vec<(Address, Vec<Val>, Val)>` (maintains backward compatibility with the old format)
//!
//! The [`ContractEvents`] struct also provides utility methods:
//! - `filter_by_contract` - filter events by contract address
//! - `events` - get the underlying XDR events
//!
//! Additionally, events defined with [`contractevent`] now have a `to_xdr` method available with
//! `testutils` feature that converts the event to its XDR representation for comparison.
//!
//! ## Example: Using the old comparison style
//!
//! The old comparison style using `Vec<(Address, Vec<Val>, Val)>` still works:
//!
//! ```
//! # #![cfg(feature = "testutils")]
//! # use soroban_sdk::{contract, contractevent, contractimpl, symbol_short, vec, Env, Address, IntoVal, Symbol, Val, testutils::{Address as _, Events as _}};
//! #
//! # #[contract]
//! # pub struct Contract;
//! #
//! # fn main() {
//! # let env = Env::default();
//! # let id = env.register(Contract, ());
//! # env.as_contract(&id, || {
//! #[contractevent]
//! pub struct MyEvent {
//! #[topic]
//! name: Symbol,
//! value: u32,
//! }
//!
//! MyEvent {
//! name: symbol_short!("hello"),
//! value: 42,
//! }.publish(&env);
//! # });
//!
//! // The old comparison style still works:
//! use soroban_sdk::Map;
//! assert_eq!(
//! env.events().all(),
//! vec![&env,
//! (
//! id.clone(),
//! (symbol_short!("my_event"), symbol_short!("hello")).into_val(&env),
//! Map::<Symbol, Val>::from_array(&env, [
//! (symbol_short!("value"), 42u32.into())
//! ]).into_val(&env),
//! ),
//! ]
//! );
//! # }
//! ```
//!
//! ## Example: Using XDR comparison (new recommended style)
//!
//! The new style uses `to_xdr` on the event for cleaner assertions:
//!
//! ```
//! # #![cfg(feature = "testutils")]
//! # use soroban_sdk::{contract, contractevent, contractimpl, symbol_short, vec, Env, Address, IntoVal, Symbol, Val, testutils::{Address as _, Events as _}, Event};
//! #
//! # #[contract]
//! # pub struct Contract;
//! #
//! # fn main() {
//! # let env = Env::default();
//! # let id = env.register(Contract, ());
//! #
//! #[contractevent]
//! pub struct MyEvent {
//! #[topic]
//! name: Symbol,
//! value: u32,
//! }
//!
//! let event = MyEvent {
//! name: symbol_short!("hello"),
//! value: 42,
//! };
//!
//! # env.as_contract(&id, || {
//! event.publish(&env);
//! # });
//!
//! // New style: compare with XDR directly
//! assert_eq!(
//! env.events().all(),
//! std::vec![event.to_xdr(&env, &id)],
//! );
//! # }
//! ```
//!
//! ## Example: Filtering events by contract
//!
//! Use `filter_by_contract` to get events from a specific contract:
//!
//! ```
//! # #![cfg(feature = "testutils")]
//! # use soroban_sdk::{contract, contractevent, contractimpl, symbol_short, vec, Env, Address, IntoVal, Symbol, Val, testutils::{Address as _, Events as _}, Event};
//! #
//! # #[contract]
//! # pub struct Contract;
//! #
//! # fn main() {
//! # let env = Env::default();
//! # let contract_a = env.register(Contract, ());
//! # let contract_b = env.register(Contract, ());
//! #
//! #[contractevent]
//! pub struct MyEvent {
//! #[topic]
//! name: Symbol,
//! value: u32,
//! }
//!
//! let event_a = MyEvent {
//! name: symbol_short!("hello"),
//! value: 1,
//! };
//! let event_b = MyEvent {
//! name: symbol_short!("world"),
//! value: 2,
//! };
//!
//! env.as_contract(&contract_a, || {
//! event_a.publish(&env);
//! env.as_contract(&contract_b, || {
//! event_b.publish(&env);
//! });
//! });
//!
//! // Filter to get only events from contract_a
//! assert_eq!(
//! env.events().all().filter_by_contract(&contract_a),
//! std::vec![event_a.to_xdr(&env, &contract_a)],
//! );
//!
//! // Filter to get only events from contract_b
//! assert_eq!(
//! env.events().all().filter_by_contract(&contract_b),
//! std::vec![event_b.to_xdr(&env, &contract_b)],
//! );
//! # }
//! ```
//!
//! [`ContractEvents`]: crate::testutils::ContractEvents
//! [`contractevent`]: crate::contractevent