@@ -1191,3 +1191,136 @@ impl Default for MainThreadValidator {
1191
1191
}
1192
1192
}
1193
1193
}
1194
+
1195
+ #[ cfg( test) ]
1196
+ mod tests {
1197
+ use super :: World ;
1198
+ use bevy_ecs_macros:: Component ;
1199
+ use std:: {
1200
+ panic,
1201
+ sync:: {
1202
+ atomic:: { AtomicBool , Ordering } ,
1203
+ Arc , Mutex ,
1204
+ } ,
1205
+ } ;
1206
+
1207
+ // For bevy_ecs_macros
1208
+ use crate as bevy_ecs;
1209
+
1210
+ type ID = u8 ;
1211
+
1212
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
1213
+ enum DropLogItem {
1214
+ Create ( ID ) ,
1215
+ Drop ( ID ) ,
1216
+ }
1217
+
1218
+ #[ derive( Component ) ]
1219
+ struct MayPanicInDrop {
1220
+ drop_log : Arc < Mutex < Vec < DropLogItem > > > ,
1221
+ expected_panic_flag : Arc < AtomicBool > ,
1222
+ should_panic : bool ,
1223
+ id : u8 ,
1224
+ }
1225
+
1226
+ impl MayPanicInDrop {
1227
+ fn new (
1228
+ drop_log : & Arc < Mutex < Vec < DropLogItem > > > ,
1229
+ expected_panic_flag : & Arc < AtomicBool > ,
1230
+ should_panic : bool ,
1231
+ id : u8 ,
1232
+ ) -> Self {
1233
+ println ! ( "creating component with id {}" , id) ;
1234
+ drop_log. lock ( ) . unwrap ( ) . push ( DropLogItem :: Create ( id) ) ;
1235
+
1236
+ Self {
1237
+ drop_log : Arc :: clone ( drop_log) ,
1238
+ expected_panic_flag : Arc :: clone ( expected_panic_flag) ,
1239
+ should_panic,
1240
+ id,
1241
+ }
1242
+ }
1243
+ }
1244
+
1245
+ impl Drop for MayPanicInDrop {
1246
+ fn drop ( & mut self ) {
1247
+ println ! ( "dropping component with id {}" , self . id) ;
1248
+
1249
+ {
1250
+ let mut drop_log = self . drop_log . lock ( ) . unwrap ( ) ;
1251
+ drop_log. push ( DropLogItem :: Drop ( self . id ) ) ;
1252
+ // Don't keep the mutex while panicking, or we'll poison it.
1253
+ drop ( drop_log) ;
1254
+ }
1255
+
1256
+ if self . should_panic {
1257
+ self . expected_panic_flag . store ( true , Ordering :: SeqCst ) ;
1258
+ panic ! ( "testing what happens on panic inside drop" ) ;
1259
+ }
1260
+ }
1261
+ }
1262
+
1263
+ struct DropTestHelper {
1264
+ drop_log : Arc < Mutex < Vec < DropLogItem > > > ,
1265
+ /// Set to `true` right before we intentionally panic, so that if we get
1266
+ /// a panic, we know if it was intended or not.
1267
+ expected_panic_flag : Arc < AtomicBool > ,
1268
+ }
1269
+
1270
+ impl DropTestHelper {
1271
+ pub fn new ( ) -> Self {
1272
+ Self {
1273
+ drop_log : Arc :: new ( Mutex :: new ( Vec :: < DropLogItem > :: new ( ) ) ) ,
1274
+ expected_panic_flag : Arc :: new ( AtomicBool :: new ( false ) ) ,
1275
+ }
1276
+ }
1277
+
1278
+ pub fn make_component ( & self , should_panic : bool , id : ID ) -> MayPanicInDrop {
1279
+ MayPanicInDrop :: new ( & self . drop_log , & self . expected_panic_flag , should_panic, id)
1280
+ }
1281
+
1282
+ pub fn finish ( self , panic_res : std:: thread:: Result < ( ) > ) -> Vec < DropLogItem > {
1283
+ let drop_log = Arc :: try_unwrap ( self . drop_log )
1284
+ . unwrap ( )
1285
+ . into_inner ( )
1286
+ . unwrap ( ) ;
1287
+ let expected_panic_flag = self . expected_panic_flag . load ( Ordering :: SeqCst ) ;
1288
+
1289
+ if !expected_panic_flag {
1290
+ match panic_res {
1291
+ Ok ( ( ) ) => panic ! ( "Expected a panic but it didn't happen" ) ,
1292
+ Err ( e) => panic:: resume_unwind ( e) ,
1293
+ }
1294
+ }
1295
+
1296
+ drop_log
1297
+ }
1298
+ }
1299
+
1300
+ #[ test]
1301
+ fn panic_while_overwriting_component ( ) {
1302
+ let helper = DropTestHelper :: new ( ) ;
1303
+
1304
+ let res = panic:: catch_unwind ( || {
1305
+ let mut world = World :: new ( ) ;
1306
+ world
1307
+ . spawn ( )
1308
+ . insert ( helper. make_component ( true , 0 ) )
1309
+ . insert ( helper. make_component ( false , 1 ) ) ;
1310
+
1311
+ println ! ( "Done inserting! Dropping world..." ) ;
1312
+ } ) ;
1313
+
1314
+ let drop_log = helper. finish ( res) ;
1315
+
1316
+ assert_eq ! (
1317
+ & * drop_log,
1318
+ [
1319
+ DropLogItem :: Create ( 0 ) ,
1320
+ DropLogItem :: Create ( 1 ) ,
1321
+ DropLogItem :: Drop ( 0 ) ,
1322
+ DropLogItem :: Drop ( 1 )
1323
+ ]
1324
+ ) ;
1325
+ }
1326
+ }
0 commit comments