1pub type GoudErrorCode = i32;
31
32pub const SUCCESS: GoudErrorCode = 0;
38
39pub const CONTEXT_ERROR_BASE: GoudErrorCode = 1;
45
46pub const ERR_NOT_INITIALIZED: GoudErrorCode = 1;
48
49pub const ERR_ALREADY_INITIALIZED: GoudErrorCode = 2;
51
52pub const ERR_INVALID_CONTEXT: GoudErrorCode = 3;
54
55pub const ERR_CONTEXT_DESTROYED: GoudErrorCode = 4;
57
58pub const ERR_INITIALIZATION_FAILED: GoudErrorCode = 10;
61
62pub const RESOURCE_ERROR_BASE: GoudErrorCode = 100;
68
69pub const ERR_RESOURCE_NOT_FOUND: GoudErrorCode = 100;
71
72pub const ERR_RESOURCE_LOAD_FAILED: GoudErrorCode = 101;
74
75pub const ERR_RESOURCE_INVALID_FORMAT: GoudErrorCode = 102;
77
78pub const ERR_RESOURCE_ALREADY_EXISTS: GoudErrorCode = 103;
80
81pub const ERR_INVALID_HANDLE: GoudErrorCode = 110;
83
84pub const ERR_HANDLE_EXPIRED: GoudErrorCode = 111;
86
87pub const ERR_HANDLE_TYPE_MISMATCH: GoudErrorCode = 112;
89
90pub const GRAPHICS_ERROR_BASE: GoudErrorCode = 200;
96
97pub const ERR_SHADER_COMPILATION_FAILED: GoudErrorCode = 200;
99
100pub const ERR_SHADER_LINK_FAILED: GoudErrorCode = 201;
102
103pub const ERR_TEXTURE_CREATION_FAILED: GoudErrorCode = 210;
105
106pub const ERR_BUFFER_CREATION_FAILED: GoudErrorCode = 211;
108
109pub const ERR_RENDER_TARGET_FAILED: GoudErrorCode = 220;
111
112pub const ERR_BACKEND_NOT_SUPPORTED: GoudErrorCode = 230;
114
115pub const ERR_DRAW_CALL_FAILED: GoudErrorCode = 240;
117
118pub const ENTITY_ERROR_BASE: GoudErrorCode = 300;
124
125pub const ERR_ENTITY_NOT_FOUND: GoudErrorCode = 300;
127
128pub const ERR_ENTITY_ALREADY_EXISTS: GoudErrorCode = 301;
130
131pub const ERR_COMPONENT_NOT_FOUND: GoudErrorCode = 310;
133
134pub const ERR_COMPONENT_ALREADY_EXISTS: GoudErrorCode = 311;
136
137pub const ERR_QUERY_FAILED: GoudErrorCode = 320;
139
140pub const INPUT_ERROR_BASE: GoudErrorCode = 400;
146
147pub const ERR_INPUT_DEVICE_NOT_FOUND: GoudErrorCode = 400;
149
150pub const ERR_INVALID_INPUT_ACTION: GoudErrorCode = 401;
152
153pub const SYSTEM_ERROR_BASE: GoudErrorCode = 500;
159
160pub const ERR_WINDOW_CREATION_FAILED: GoudErrorCode = 500;
162
163pub const ERR_AUDIO_INIT_FAILED: GoudErrorCode = 510;
165
166pub const ERR_PHYSICS_INIT_FAILED: GoudErrorCode = 520;
168
169pub const ERR_PLATFORM_ERROR: GoudErrorCode = 530;
171
172pub const INTERNAL_ERROR_BASE: GoudErrorCode = 900;
178
179pub const ERR_INTERNAL_ERROR: GoudErrorCode = 900;
181
182pub const ERR_NOT_IMPLEMENTED: GoudErrorCode = 901;
184
185pub const ERR_INVALID_STATE: GoudErrorCode = 902;
187
188#[derive(Debug, Clone, PartialEq, Eq)]
218pub enum GoudError {
219 NotInitialized,
227
228 AlreadyInitialized,
233
234 InvalidContext,
238
239 ContextDestroyed,
244
245 InitializationFailed(String),
251
252 ResourceNotFound(String),
261
262 ResourceLoadFailed(String),
268
269 ResourceInvalidFormat(String),
275
276 ResourceAlreadyExists(String),
282
283 InvalidHandle,
288
289 HandleExpired,
295
296 HandleTypeMismatch,
301
302 ShaderCompilationFailed(String),
311
312 ShaderLinkFailed(String),
319
320 TextureCreationFailed(String),
327
328 BufferCreationFailed(String),
335
336 RenderTargetFailed(String),
343
344 BackendNotSupported(String),
351
352 DrawCallFailed(String),
359
360 EntityNotFound,
368
369 EntityAlreadyExists,
375
376 ComponentNotFound,
381
382 ComponentAlreadyExists,
387
388 QueryFailed(String),
395
396 WindowCreationFailed(String),
406
407 AudioInitFailed(String),
414
415 PhysicsInitFailed(String),
422
423 PlatformError(String),
429
430 InternalError(String),
440
441 NotImplemented(String),
448
449 InvalidState(String),
456}
457
458impl GoudError {
459 #[inline]
477 pub const fn error_code(&self) -> GoudErrorCode {
478 match self {
479 GoudError::NotInitialized => ERR_NOT_INITIALIZED,
481 GoudError::AlreadyInitialized => ERR_ALREADY_INITIALIZED,
482 GoudError::InvalidContext => ERR_INVALID_CONTEXT,
483 GoudError::ContextDestroyed => ERR_CONTEXT_DESTROYED,
484 GoudError::InitializationFailed(_) => ERR_INITIALIZATION_FAILED,
485
486 GoudError::ResourceNotFound(_) => ERR_RESOURCE_NOT_FOUND,
488 GoudError::ResourceLoadFailed(_) => ERR_RESOURCE_LOAD_FAILED,
489 GoudError::ResourceInvalidFormat(_) => ERR_RESOURCE_INVALID_FORMAT,
490 GoudError::ResourceAlreadyExists(_) => ERR_RESOURCE_ALREADY_EXISTS,
491 GoudError::InvalidHandle => ERR_INVALID_HANDLE,
492 GoudError::HandleExpired => ERR_HANDLE_EXPIRED,
493 GoudError::HandleTypeMismatch => ERR_HANDLE_TYPE_MISMATCH,
494
495 GoudError::ShaderCompilationFailed(_) => ERR_SHADER_COMPILATION_FAILED,
497 GoudError::ShaderLinkFailed(_) => ERR_SHADER_LINK_FAILED,
498 GoudError::TextureCreationFailed(_) => ERR_TEXTURE_CREATION_FAILED,
499 GoudError::BufferCreationFailed(_) => ERR_BUFFER_CREATION_FAILED,
500 GoudError::RenderTargetFailed(_) => ERR_RENDER_TARGET_FAILED,
501 GoudError::BackendNotSupported(_) => ERR_BACKEND_NOT_SUPPORTED,
502 GoudError::DrawCallFailed(_) => ERR_DRAW_CALL_FAILED,
503
504 GoudError::EntityNotFound => ERR_ENTITY_NOT_FOUND,
506 GoudError::EntityAlreadyExists => ERR_ENTITY_ALREADY_EXISTS,
507 GoudError::ComponentNotFound => ERR_COMPONENT_NOT_FOUND,
508 GoudError::ComponentAlreadyExists => ERR_COMPONENT_ALREADY_EXISTS,
509 GoudError::QueryFailed(_) => ERR_QUERY_FAILED,
510
511 GoudError::WindowCreationFailed(_) => ERR_WINDOW_CREATION_FAILED,
513 GoudError::AudioInitFailed(_) => ERR_AUDIO_INIT_FAILED,
514 GoudError::PhysicsInitFailed(_) => ERR_PHYSICS_INIT_FAILED,
515 GoudError::PlatformError(_) => ERR_PLATFORM_ERROR,
516
517 GoudError::InternalError(_) => ERR_INTERNAL_ERROR,
519 GoudError::NotImplemented(_) => ERR_NOT_IMPLEMENTED,
520 GoudError::InvalidState(_) => ERR_INVALID_STATE,
521 }
522 }
523
524 #[inline]
536 pub const fn category(&self) -> &'static str {
537 error_category(self.error_code())
538 }
539
540 pub fn message(&self) -> &str {
557 match self {
558 GoudError::NotInitialized => "Engine has not been initialized",
560 GoudError::AlreadyInitialized => "Engine has already been initialized",
561 GoudError::InvalidContext => "Invalid engine context",
562 GoudError::ContextDestroyed => "Engine context has been destroyed",
563 GoudError::InitializationFailed(msg) => msg,
564
565 GoudError::ResourceNotFound(msg) => msg,
567 GoudError::ResourceLoadFailed(msg) => msg,
568 GoudError::ResourceInvalidFormat(msg) => msg,
569 GoudError::ResourceAlreadyExists(msg) => msg,
570 GoudError::InvalidHandle => "Invalid handle",
571 GoudError::HandleExpired => "Handle has expired",
572 GoudError::HandleTypeMismatch => "Handle type mismatch",
573
574 GoudError::ShaderCompilationFailed(msg) => msg,
576 GoudError::ShaderLinkFailed(msg) => msg,
577 GoudError::TextureCreationFailed(msg) => msg,
578 GoudError::BufferCreationFailed(msg) => msg,
579 GoudError::RenderTargetFailed(msg) => msg,
580 GoudError::BackendNotSupported(msg) => msg,
581 GoudError::DrawCallFailed(msg) => msg,
582
583 GoudError::EntityNotFound => "Entity not found",
585 GoudError::EntityAlreadyExists => "Entity already exists",
586 GoudError::ComponentNotFound => "Component not found",
587 GoudError::ComponentAlreadyExists => "Component already exists",
588 GoudError::QueryFailed(msg) => msg,
589
590 GoudError::WindowCreationFailed(msg) => msg,
592 GoudError::AudioInitFailed(msg) => msg,
593 GoudError::PhysicsInitFailed(msg) => msg,
594 GoudError::PlatformError(msg) => msg,
595
596 GoudError::InternalError(msg) => msg,
598 GoudError::NotImplemented(msg) => msg,
599 GoudError::InvalidState(msg) => msg,
600 }
601 }
602}
603
604impl std::fmt::Display for GoudError {
609 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
624 write!(
625 f,
626 "[GOUD-{}] {}: {}",
627 self.error_code(),
628 self.category(),
629 self.message()
630 )
631 }
632}
633
634impl std::error::Error for GoudError {
635 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
640 None
641 }
642}
643
644impl From<std::io::Error> for GoudError {
649 fn from(error: std::io::Error) -> Self {
667 match error.kind() {
668 std::io::ErrorKind::NotFound => GoudError::ResourceNotFound(error.to_string()),
669 std::io::ErrorKind::PermissionDenied => {
670 GoudError::ResourceLoadFailed(format!("Permission denied: {}", error))
671 }
672 _ => GoudError::ResourceLoadFailed(error.to_string()),
673 }
674 }
675}
676
677impl From<String> for GoudError {
678 fn from(msg: String) -> Self {
692 GoudError::InternalError(msg)
693 }
694}
695
696impl From<&str> for GoudError {
697 fn from(msg: &str) -> Self {
710 GoudError::InternalError(msg.to_string())
711 }
712}
713
714pub type GoudResult<T> = Result<T, GoudError>;
742
743#[inline]
758pub const fn error_category(code: GoudErrorCode) -> &'static str {
759 match code {
760 SUCCESS => "Success",
761 1..=99 => "Context",
762 100..=199 => "Resource",
763 200..=299 => "Graphics",
764 300..=399 => "Entity",
765 400..=499 => "Input",
766 500..=599 => "System",
767 900..=999 => "Internal",
768 _ => "Unknown",
769 }
770}
771
772#[inline]
774pub const fn is_success(code: GoudErrorCode) -> bool {
775 code == SUCCESS
776}
777
778#[inline]
780pub const fn is_error(code: GoudErrorCode) -> bool {
781 code != SUCCESS
782}
783
784use std::cell::RefCell;
808
809thread_local! {
810 static LAST_ERROR: RefCell<Option<GoudError>> = const { RefCell::new(None) };
815}
816
817pub fn set_last_error(error: GoudError) {
835 LAST_ERROR.with(|e| {
836 *e.borrow_mut() = Some(error);
837 });
838}
839
840pub fn take_last_error() -> Option<GoudError> {
860 LAST_ERROR.with(|e| e.borrow_mut().take())
861}
862
863pub fn get_last_error() -> Option<GoudError> {
883 LAST_ERROR.with(|e| e.borrow().clone())
884}
885
886pub fn last_error_code() -> GoudErrorCode {
906 LAST_ERROR.with(|e| {
907 e.borrow()
908 .as_ref()
909 .map(|err| err.error_code())
910 .unwrap_or(SUCCESS)
911 })
912}
913
914pub fn last_error_message() -> Option<String> {
933 LAST_ERROR.with(|e| e.borrow().as_ref().map(|err| err.message().to_string()))
934}
935
936pub fn clear_last_error() {
955 LAST_ERROR.with(|e| {
956 *e.borrow_mut() = None;
957 });
958}
959
960#[repr(C)]
989#[derive(Clone, Copy, Debug, PartialEq, Eq)]
990pub struct GoudFFIResult {
991 pub code: GoudErrorCode,
993 pub success: bool,
995}
996
997impl GoudFFIResult {
998 #[inline]
1010 pub const fn success() -> Self {
1011 Self {
1012 code: SUCCESS,
1013 success: true,
1014 }
1015 }
1016
1017 #[inline]
1029 pub const fn from_code(code: GoudErrorCode) -> Self {
1030 Self {
1031 code,
1032 success: code == SUCCESS,
1033 }
1034 }
1035
1036 #[inline]
1050 pub fn from_error(error: GoudError) -> Self {
1051 let code = error.error_code();
1052 set_last_error(error);
1053 Self {
1054 code,
1055 success: false,
1056 }
1057 }
1058
1059 #[inline]
1078 pub fn from_result<T>(result: GoudResult<T>) -> Self {
1079 match result {
1080 Ok(_) => {
1081 clear_last_error();
1082 Self::success()
1083 }
1084 Err(error) => Self::from_error(error),
1085 }
1086 }
1087
1088 #[inline]
1090 pub const fn is_success(&self) -> bool {
1091 self.success
1092 }
1093
1094 #[inline]
1096 pub const fn is_error(&self) -> bool {
1097 !self.success
1098 }
1099}
1100
1101impl Default for GoudFFIResult {
1102 fn default() -> Self {
1104 Self::success()
1105 }
1106}
1107
1108impl From<GoudError> for GoudFFIResult {
1109 fn from(error: GoudError) -> Self {
1110 Self::from_error(error)
1111 }
1112}
1113
1114impl<T> From<GoudResult<T>> for GoudFFIResult {
1115 fn from(result: GoudResult<T>) -> Self {
1116 Self::from_result(result)
1117 }
1118}
1119
1120#[cfg(test)]
1125mod tests {
1126 use super::*;
1127
1128 #[test]
1129 fn test_success_code_is_zero() {
1130 assert_eq!(SUCCESS, 0);
1131 }
1132
1133 #[test]
1134 fn test_error_code_ranges_are_non_overlapping() {
1135 assert!(CONTEXT_ERROR_BASE < RESOURCE_ERROR_BASE);
1137 assert!(RESOURCE_ERROR_BASE < GRAPHICS_ERROR_BASE);
1138 assert!(GRAPHICS_ERROR_BASE < ENTITY_ERROR_BASE);
1139 assert!(ENTITY_ERROR_BASE < INPUT_ERROR_BASE);
1140 assert!(INPUT_ERROR_BASE < SYSTEM_ERROR_BASE);
1141 assert!(SYSTEM_ERROR_BASE < INTERNAL_ERROR_BASE);
1142 }
1143
1144 #[test]
1145 fn test_error_category_classification() {
1146 assert_eq!(error_category(SUCCESS), "Success");
1147 assert_eq!(error_category(ERR_NOT_INITIALIZED), "Context");
1148 assert_eq!(error_category(ERR_RESOURCE_NOT_FOUND), "Resource");
1149 assert_eq!(error_category(ERR_SHADER_COMPILATION_FAILED), "Graphics");
1150 assert_eq!(error_category(ERR_ENTITY_NOT_FOUND), "Entity");
1151 assert_eq!(error_category(ERR_INPUT_DEVICE_NOT_FOUND), "Input");
1152 assert_eq!(error_category(ERR_WINDOW_CREATION_FAILED), "System");
1153 assert_eq!(error_category(ERR_INTERNAL_ERROR), "Internal");
1154 }
1155
1156 #[test]
1157 fn test_is_success_and_is_error() {
1158 assert!(is_success(SUCCESS));
1159 assert!(!is_error(SUCCESS));
1160
1161 assert!(!is_success(ERR_NOT_INITIALIZED));
1162 assert!(is_error(ERR_NOT_INITIALIZED));
1163
1164 assert!(!is_success(ERR_RESOURCE_NOT_FOUND));
1165 assert!(is_error(ERR_RESOURCE_NOT_FOUND));
1166 }
1167
1168 #[test]
1169 fn test_error_codes_within_category_bounds() {
1170 assert!(ERR_NOT_INITIALIZED >= 1 && ERR_NOT_INITIALIZED < 100);
1172 assert!(ERR_INITIALIZATION_FAILED >= 1 && ERR_INITIALIZATION_FAILED < 100);
1173
1174 assert!(ERR_RESOURCE_NOT_FOUND >= 100 && ERR_RESOURCE_NOT_FOUND < 200);
1176 assert!(ERR_HANDLE_TYPE_MISMATCH >= 100 && ERR_HANDLE_TYPE_MISMATCH < 200);
1177
1178 assert!(ERR_SHADER_COMPILATION_FAILED >= 200 && ERR_SHADER_COMPILATION_FAILED < 300);
1180 assert!(ERR_DRAW_CALL_FAILED >= 200 && ERR_DRAW_CALL_FAILED < 300);
1181
1182 assert!(ERR_ENTITY_NOT_FOUND >= 300 && ERR_ENTITY_NOT_FOUND < 400);
1184 assert!(ERR_QUERY_FAILED >= 300 && ERR_QUERY_FAILED < 400);
1185
1186 assert!(ERR_INPUT_DEVICE_NOT_FOUND >= 400 && ERR_INPUT_DEVICE_NOT_FOUND < 500);
1188
1189 assert!(ERR_WINDOW_CREATION_FAILED >= 500 && ERR_WINDOW_CREATION_FAILED < 600);
1191 assert!(ERR_PLATFORM_ERROR >= 500 && ERR_PLATFORM_ERROR < 600);
1192
1193 assert!(ERR_INTERNAL_ERROR >= 900 && ERR_INTERNAL_ERROR < 1000);
1195 assert!(ERR_INVALID_STATE >= 900 && ERR_INVALID_STATE < 1000);
1196 }
1197
1198 #[test]
1199 fn test_unknown_category_for_out_of_range() {
1200 assert_eq!(error_category(-1), "Unknown");
1201 assert_eq!(error_category(1000), "Unknown");
1202 assert_eq!(error_category(600), "Unknown");
1203 }
1204
1205 mod context_errors {
1210 use super::*;
1211
1212 #[test]
1213 fn test_not_initialized_error_code() {
1214 let error = GoudError::NotInitialized;
1215 assert_eq!(error.error_code(), ERR_NOT_INITIALIZED);
1216 assert_eq!(error.error_code(), 1);
1217 }
1218
1219 #[test]
1220 fn test_already_initialized_error_code() {
1221 let error = GoudError::AlreadyInitialized;
1222 assert_eq!(error.error_code(), ERR_ALREADY_INITIALIZED);
1223 assert_eq!(error.error_code(), 2);
1224 }
1225
1226 #[test]
1227 fn test_invalid_context_error_code() {
1228 let error = GoudError::InvalidContext;
1229 assert_eq!(error.error_code(), ERR_INVALID_CONTEXT);
1230 assert_eq!(error.error_code(), 3);
1231 }
1232
1233 #[test]
1234 fn test_context_destroyed_error_code() {
1235 let error = GoudError::ContextDestroyed;
1236 assert_eq!(error.error_code(), ERR_CONTEXT_DESTROYED);
1237 assert_eq!(error.error_code(), 4);
1238 }
1239
1240 #[test]
1241 fn test_initialization_failed_error_code() {
1242 let error = GoudError::InitializationFailed("GPU not found".to_string());
1243 assert_eq!(error.error_code(), ERR_INITIALIZATION_FAILED);
1244 assert_eq!(error.error_code(), 10);
1245
1246 let error2 = GoudError::InitializationFailed("Missing dependency".to_string());
1248 assert_eq!(error2.error_code(), ERR_INITIALIZATION_FAILED);
1249 }
1250
1251 #[test]
1252 fn test_all_context_errors_in_context_category() {
1253 let errors = [
1254 GoudError::NotInitialized,
1255 GoudError::AlreadyInitialized,
1256 GoudError::InvalidContext,
1257 GoudError::ContextDestroyed,
1258 GoudError::InitializationFailed("test".to_string()),
1259 ];
1260
1261 for error in errors {
1262 assert_eq!(
1263 error.category(),
1264 "Context",
1265 "Error {:?} should be in Context category",
1266 error
1267 );
1268 }
1269 }
1270
1271 #[test]
1272 fn test_context_error_codes_in_valid_range() {
1273 let errors = [
1274 GoudError::NotInitialized,
1275 GoudError::AlreadyInitialized,
1276 GoudError::InvalidContext,
1277 GoudError::ContextDestroyed,
1278 GoudError::InitializationFailed("test".to_string()),
1279 ];
1280
1281 for error in errors {
1282 let code = error.error_code();
1283 assert!(
1284 code >= 1 && code < 100,
1285 "Context error {:?} has code {} which is outside range 1-99",
1286 error,
1287 code
1288 );
1289 }
1290 }
1291
1292 #[test]
1293 fn test_goud_error_derives() {
1294 let error = GoudError::NotInitialized;
1296 let debug_str = format!("{:?}", error);
1297 assert!(debug_str.contains("NotInitialized"));
1298
1299 let cloned = error.clone();
1301 assert_eq!(error, cloned);
1302
1303 assert_eq!(GoudError::NotInitialized, GoudError::NotInitialized);
1305 assert_ne!(GoudError::NotInitialized, GoudError::AlreadyInitialized);
1306
1307 let err1 = GoudError::InitializationFailed("msg1".to_string());
1309 let err2 = GoudError::InitializationFailed("msg1".to_string());
1310 let err3 = GoudError::InitializationFailed("msg2".to_string());
1311 assert_eq!(err1, err2);
1312 assert_ne!(err1, err3);
1313 }
1314
1315 #[test]
1316 fn test_initialization_failed_preserves_message() {
1317 let message = "Failed to initialize OpenGL context: version 4.5 required";
1318 let error = GoudError::InitializationFailed(message.to_string());
1319
1320 if let GoudError::InitializationFailed(msg) = error {
1322 assert_eq!(msg, message);
1323 } else {
1324 panic!("Expected InitializationFailed variant");
1325 }
1326 }
1327 }
1328
1329 mod resource_errors {
1334 use super::*;
1335
1336 #[test]
1337 fn test_resource_not_found_error_code() {
1338 let error = GoudError::ResourceNotFound("textures/player.png".to_string());
1339 assert_eq!(error.error_code(), ERR_RESOURCE_NOT_FOUND);
1340 assert_eq!(error.error_code(), 100);
1341 }
1342
1343 #[test]
1344 fn test_resource_load_failed_error_code() {
1345 let error = GoudError::ResourceLoadFailed("I/O error reading file".to_string());
1346 assert_eq!(error.error_code(), ERR_RESOURCE_LOAD_FAILED);
1347 assert_eq!(error.error_code(), 101);
1348 }
1349
1350 #[test]
1351 fn test_resource_invalid_format_error_code() {
1352 let error = GoudError::ResourceInvalidFormat("Invalid PNG header".to_string());
1353 assert_eq!(error.error_code(), ERR_RESOURCE_INVALID_FORMAT);
1354 assert_eq!(error.error_code(), 102);
1355 }
1356
1357 #[test]
1358 fn test_resource_already_exists_error_code() {
1359 let error = GoudError::ResourceAlreadyExists("player_texture".to_string());
1360 assert_eq!(error.error_code(), ERR_RESOURCE_ALREADY_EXISTS);
1361 assert_eq!(error.error_code(), 103);
1362 }
1363
1364 #[test]
1365 fn test_invalid_handle_error_code() {
1366 let error = GoudError::InvalidHandle;
1367 assert_eq!(error.error_code(), ERR_INVALID_HANDLE);
1368 assert_eq!(error.error_code(), 110);
1369 }
1370
1371 #[test]
1372 fn test_handle_expired_error_code() {
1373 let error = GoudError::HandleExpired;
1374 assert_eq!(error.error_code(), ERR_HANDLE_EXPIRED);
1375 assert_eq!(error.error_code(), 111);
1376 }
1377
1378 #[test]
1379 fn test_handle_type_mismatch_error_code() {
1380 let error = GoudError::HandleTypeMismatch;
1381 assert_eq!(error.error_code(), ERR_HANDLE_TYPE_MISMATCH);
1382 assert_eq!(error.error_code(), 112);
1383 }
1384
1385 #[test]
1386 fn test_all_resource_errors_in_resource_category() {
1387 let errors: Vec<GoudError> = vec![
1388 GoudError::ResourceNotFound("test".to_string()),
1389 GoudError::ResourceLoadFailed("test".to_string()),
1390 GoudError::ResourceInvalidFormat("test".to_string()),
1391 GoudError::ResourceAlreadyExists("test".to_string()),
1392 GoudError::InvalidHandle,
1393 GoudError::HandleExpired,
1394 GoudError::HandleTypeMismatch,
1395 ];
1396
1397 for error in errors {
1398 assert_eq!(
1399 error.category(),
1400 "Resource",
1401 "Error {:?} should be in Resource category",
1402 error
1403 );
1404 }
1405 }
1406
1407 #[test]
1408 fn test_resource_error_codes_in_valid_range() {
1409 let errors: Vec<GoudError> = vec![
1410 GoudError::ResourceNotFound("test".to_string()),
1411 GoudError::ResourceLoadFailed("test".to_string()),
1412 GoudError::ResourceInvalidFormat("test".to_string()),
1413 GoudError::ResourceAlreadyExists("test".to_string()),
1414 GoudError::InvalidHandle,
1415 GoudError::HandleExpired,
1416 GoudError::HandleTypeMismatch,
1417 ];
1418
1419 for error in errors {
1420 let code = error.error_code();
1421 assert!(
1422 code >= 100 && code < 200,
1423 "Resource error {:?} has code {} which is outside range 100-199",
1424 error,
1425 code
1426 );
1427 }
1428 }
1429
1430 #[test]
1431 fn test_resource_errors_preserve_message() {
1432 let path = "assets/textures/missing.png";
1434 if let GoudError::ResourceNotFound(msg) = GoudError::ResourceNotFound(path.to_string())
1435 {
1436 assert_eq!(msg, path);
1437 } else {
1438 panic!("Expected ResourceNotFound variant");
1439 }
1440
1441 let reason = "Permission denied";
1443 if let GoudError::ResourceLoadFailed(msg) =
1444 GoudError::ResourceLoadFailed(reason.to_string())
1445 {
1446 assert_eq!(msg, reason);
1447 } else {
1448 panic!("Expected ResourceLoadFailed variant");
1449 }
1450
1451 let format_err = "Unsupported texture format: PVRTC";
1453 if let GoudError::ResourceInvalidFormat(msg) =
1454 GoudError::ResourceInvalidFormat(format_err.to_string())
1455 {
1456 assert_eq!(msg, format_err);
1457 } else {
1458 panic!("Expected ResourceInvalidFormat variant");
1459 }
1460
1461 let resource_id = "main_shader";
1463 if let GoudError::ResourceAlreadyExists(msg) =
1464 GoudError::ResourceAlreadyExists(resource_id.to_string())
1465 {
1466 assert_eq!(msg, resource_id);
1467 } else {
1468 panic!("Expected ResourceAlreadyExists variant");
1469 }
1470 }
1471
1472 #[test]
1473 fn test_resource_error_equality() {
1474 let err1 = GoudError::ResourceNotFound("file.txt".to_string());
1476 let err2 = GoudError::ResourceNotFound("file.txt".to_string());
1477 assert_eq!(err1, err2);
1478
1479 let err3 = GoudError::ResourceNotFound("other.txt".to_string());
1481 assert_ne!(err1, err3);
1482
1483 let err4 = GoudError::ResourceLoadFailed("file.txt".to_string());
1485 assert_ne!(err1, err4);
1486
1487 assert_eq!(GoudError::InvalidHandle, GoudError::InvalidHandle);
1489 assert_ne!(GoudError::InvalidHandle, GoudError::HandleExpired);
1490 assert_ne!(GoudError::HandleExpired, GoudError::HandleTypeMismatch);
1491 }
1492
1493 #[test]
1494 fn test_resource_error_debug_format() {
1495 let error = GoudError::ResourceNotFound("test.png".to_string());
1496 let debug_str = format!("{:?}", error);
1497 assert!(debug_str.contains("ResourceNotFound"));
1498 assert!(debug_str.contains("test.png"));
1499
1500 let handle_error = GoudError::InvalidHandle;
1501 let debug_str = format!("{:?}", handle_error);
1502 assert!(debug_str.contains("InvalidHandle"));
1503 }
1504
1505 #[test]
1506 fn test_handle_error_codes_are_distinct() {
1507 assert_ne!(ERR_INVALID_HANDLE, ERR_HANDLE_EXPIRED);
1509 assert_ne!(ERR_HANDLE_EXPIRED, ERR_HANDLE_TYPE_MISMATCH);
1510 assert_ne!(ERR_INVALID_HANDLE, ERR_HANDLE_TYPE_MISMATCH);
1511
1512 assert!(ERR_INVALID_HANDLE >= 110);
1514 assert!(ERR_HANDLE_EXPIRED >= 110);
1515 assert!(ERR_HANDLE_TYPE_MISMATCH >= 110);
1516 }
1517 }
1518
1519 mod graphics_errors {
1524 use super::*;
1525
1526 #[test]
1527 fn test_shader_compilation_failed_error_code() {
1528 let error = GoudError::ShaderCompilationFailed(
1529 "ERROR: 0:15: 'vec3' : undeclared identifier".to_string(),
1530 );
1531 assert_eq!(error.error_code(), ERR_SHADER_COMPILATION_FAILED);
1532 assert_eq!(error.error_code(), 200);
1533 }
1534
1535 #[test]
1536 fn test_shader_link_failed_error_code() {
1537 let error = GoudError::ShaderLinkFailed(
1538 "ERROR: Varying variable 'vTexCoord' not written".to_string(),
1539 );
1540 assert_eq!(error.error_code(), ERR_SHADER_LINK_FAILED);
1541 assert_eq!(error.error_code(), 201);
1542 }
1543
1544 #[test]
1545 fn test_texture_creation_failed_error_code() {
1546 let error =
1547 GoudError::TextureCreationFailed("GL_OUT_OF_MEMORY: 4096x4096 RGBA8".to_string());
1548 assert_eq!(error.error_code(), ERR_TEXTURE_CREATION_FAILED);
1549 assert_eq!(error.error_code(), 210);
1550 }
1551
1552 #[test]
1553 fn test_buffer_creation_failed_error_code() {
1554 let error = GoudError::BufferCreationFailed(
1555 "Failed to allocate 256MB vertex buffer".to_string(),
1556 );
1557 assert_eq!(error.error_code(), ERR_BUFFER_CREATION_FAILED);
1558 assert_eq!(error.error_code(), 211);
1559 }
1560
1561 #[test]
1562 fn test_render_target_failed_error_code() {
1563 let error =
1564 GoudError::RenderTargetFailed("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT".to_string());
1565 assert_eq!(error.error_code(), ERR_RENDER_TARGET_FAILED);
1566 assert_eq!(error.error_code(), 220);
1567 }
1568
1569 #[test]
1570 fn test_backend_not_supported_error_code() {
1571 let error =
1572 GoudError::BackendNotSupported("Vulkan 1.2 required, found 1.0".to_string());
1573 assert_eq!(error.error_code(), ERR_BACKEND_NOT_SUPPORTED);
1574 assert_eq!(error.error_code(), 230);
1575 }
1576
1577 #[test]
1578 fn test_draw_call_failed_error_code() {
1579 let error = GoudError::DrawCallFailed(
1580 "glDrawElements failed: GL_INVALID_OPERATION".to_string(),
1581 );
1582 assert_eq!(error.error_code(), ERR_DRAW_CALL_FAILED);
1583 assert_eq!(error.error_code(), 240);
1584 }
1585
1586 #[test]
1587 fn test_all_graphics_errors_in_graphics_category() {
1588 let errors: Vec<GoudError> = vec![
1589 GoudError::ShaderCompilationFailed("test".to_string()),
1590 GoudError::ShaderLinkFailed("test".to_string()),
1591 GoudError::TextureCreationFailed("test".to_string()),
1592 GoudError::BufferCreationFailed("test".to_string()),
1593 GoudError::RenderTargetFailed("test".to_string()),
1594 GoudError::BackendNotSupported("test".to_string()),
1595 GoudError::DrawCallFailed("test".to_string()),
1596 ];
1597
1598 for error in errors {
1599 assert_eq!(
1600 error.category(),
1601 "Graphics",
1602 "Error {:?} should be in Graphics category",
1603 error
1604 );
1605 }
1606 }
1607
1608 #[test]
1609 fn test_graphics_error_codes_in_valid_range() {
1610 let errors: Vec<GoudError> = vec![
1611 GoudError::ShaderCompilationFailed("test".to_string()),
1612 GoudError::ShaderLinkFailed("test".to_string()),
1613 GoudError::TextureCreationFailed("test".to_string()),
1614 GoudError::BufferCreationFailed("test".to_string()),
1615 GoudError::RenderTargetFailed("test".to_string()),
1616 GoudError::BackendNotSupported("test".to_string()),
1617 GoudError::DrawCallFailed("test".to_string()),
1618 ];
1619
1620 for error in errors {
1621 let code = error.error_code();
1622 assert!(
1623 code >= 200 && code < 300,
1624 "Graphics error {:?} has code {} which is outside range 200-299",
1625 error,
1626 code
1627 );
1628 }
1629 }
1630
1631 #[test]
1632 fn test_graphics_errors_preserve_message() {
1633 let shader_err = "ERROR: 0:42: 'sampler2D' : syntax error";
1635 if let GoudError::ShaderCompilationFailed(msg) =
1636 GoudError::ShaderCompilationFailed(shader_err.to_string())
1637 {
1638 assert_eq!(msg, shader_err);
1639 assert!(msg.contains("42")); } else {
1641 panic!("Expected ShaderCompilationFailed variant");
1642 }
1643
1644 let backend_err = "Metal not available on Linux; available: OpenGL 4.5, Vulkan 1.2";
1646 if let GoudError::BackendNotSupported(msg) =
1647 GoudError::BackendNotSupported(backend_err.to_string())
1648 {
1649 assert_eq!(msg, backend_err);
1650 } else {
1651 panic!("Expected BackendNotSupported variant");
1652 }
1653 }
1654
1655 #[test]
1656 fn test_graphics_error_equality() {
1657 let err1 = GoudError::ShaderCompilationFailed("error line 10".to_string());
1659 let err2 = GoudError::ShaderCompilationFailed("error line 10".to_string());
1660 assert_eq!(err1, err2);
1661
1662 let err3 = GoudError::ShaderCompilationFailed("error line 20".to_string());
1664 assert_ne!(err1, err3);
1665
1666 let err4 = GoudError::ShaderLinkFailed("error line 10".to_string());
1668 assert_ne!(err1, err4);
1669
1670 assert_ne!(
1672 GoudError::TextureCreationFailed("fail".to_string()),
1673 GoudError::BufferCreationFailed("fail".to_string())
1674 );
1675 }
1676
1677 #[test]
1678 fn test_graphics_error_debug_format() {
1679 let error = GoudError::ShaderCompilationFailed("syntax error at line 5".to_string());
1680 let debug_str = format!("{:?}", error);
1681 assert!(debug_str.contains("ShaderCompilationFailed"));
1682 assert!(debug_str.contains("syntax error at line 5"));
1683
1684 let error2 = GoudError::DrawCallFailed("invalid state".to_string());
1685 let debug_str2 = format!("{:?}", error2);
1686 assert!(debug_str2.contains("DrawCallFailed"));
1687 assert!(debug_str2.contains("invalid state"));
1688 }
1689
1690 #[test]
1691 fn test_graphics_error_codes_are_distinct() {
1692 let codes = vec![
1694 ERR_SHADER_COMPILATION_FAILED,
1695 ERR_SHADER_LINK_FAILED,
1696 ERR_TEXTURE_CREATION_FAILED,
1697 ERR_BUFFER_CREATION_FAILED,
1698 ERR_RENDER_TARGET_FAILED,
1699 ERR_BACKEND_NOT_SUPPORTED,
1700 ERR_DRAW_CALL_FAILED,
1701 ];
1702
1703 for (i, code1) in codes.iter().enumerate() {
1705 for (j, code2) in codes.iter().enumerate() {
1706 if i != j {
1707 assert_ne!(
1708 code1, code2,
1709 "Error codes at index {} and {} should be different",
1710 i, j
1711 );
1712 }
1713 }
1714 }
1715 }
1716
1717 #[test]
1718 fn test_graphics_error_code_gaps_for_future_expansion() {
1719 assert!(ERR_SHADER_COMPILATION_FAILED == 200);
1722 assert!(ERR_SHADER_LINK_FAILED == 201);
1723
1724 assert!(ERR_TEXTURE_CREATION_FAILED == 210);
1726 assert!(ERR_BUFFER_CREATION_FAILED == 211);
1727
1728 assert!(ERR_RENDER_TARGET_FAILED == 220);
1730
1731 assert!(ERR_BACKEND_NOT_SUPPORTED == 230);
1733
1734 assert!(ERR_DRAW_CALL_FAILED == 240);
1736 }
1737 }
1738
1739 mod entity_errors {
1744 use super::*;
1745
1746 #[test]
1747 fn test_entity_not_found_error_code() {
1748 let error = GoudError::EntityNotFound;
1749 assert_eq!(error.error_code(), ERR_ENTITY_NOT_FOUND);
1750 assert_eq!(error.error_code(), 300);
1751 }
1752
1753 #[test]
1754 fn test_entity_already_exists_error_code() {
1755 let error = GoudError::EntityAlreadyExists;
1756 assert_eq!(error.error_code(), ERR_ENTITY_ALREADY_EXISTS);
1757 assert_eq!(error.error_code(), 301);
1758 }
1759
1760 #[test]
1761 fn test_component_not_found_error_code() {
1762 let error = GoudError::ComponentNotFound;
1763 assert_eq!(error.error_code(), ERR_COMPONENT_NOT_FOUND);
1764 assert_eq!(error.error_code(), 310);
1765 }
1766
1767 #[test]
1768 fn test_component_already_exists_error_code() {
1769 let error = GoudError::ComponentAlreadyExists;
1770 assert_eq!(error.error_code(), ERR_COMPONENT_ALREADY_EXISTS);
1771 assert_eq!(error.error_code(), 311);
1772 }
1773
1774 #[test]
1775 fn test_query_failed_error_code() {
1776 let error = GoudError::QueryFailed("conflicting access on Position".to_string());
1777 assert_eq!(error.error_code(), ERR_QUERY_FAILED);
1778 assert_eq!(error.error_code(), 320);
1779 }
1780
1781 #[test]
1782 fn test_all_entity_errors_in_entity_category() {
1783 let errors: Vec<GoudError> = vec![
1784 GoudError::EntityNotFound,
1785 GoudError::EntityAlreadyExists,
1786 GoudError::ComponentNotFound,
1787 GoudError::ComponentAlreadyExists,
1788 GoudError::QueryFailed("test".to_string()),
1789 ];
1790
1791 for error in errors {
1792 assert_eq!(
1793 error.category(),
1794 "Entity",
1795 "Error {:?} should be in Entity category",
1796 error
1797 );
1798 }
1799 }
1800
1801 #[test]
1802 fn test_entity_error_codes_in_valid_range() {
1803 let errors: Vec<GoudError> = vec![
1804 GoudError::EntityNotFound,
1805 GoudError::EntityAlreadyExists,
1806 GoudError::ComponentNotFound,
1807 GoudError::ComponentAlreadyExists,
1808 GoudError::QueryFailed("test".to_string()),
1809 ];
1810
1811 for error in errors {
1812 let code = error.error_code();
1813 assert!(
1814 code >= 300 && code < 400,
1815 "Entity error {:?} has code {} which is outside range 300-399",
1816 error,
1817 code
1818 );
1819 }
1820 }
1821
1822 #[test]
1823 fn test_query_failed_preserves_message() {
1824 let query_err = "Conflicting access: &mut Position and &Position on same entity";
1825 if let GoudError::QueryFailed(msg) = GoudError::QueryFailed(query_err.to_string()) {
1826 assert_eq!(msg, query_err);
1827 } else {
1828 panic!("Expected QueryFailed variant");
1829 }
1830 }
1831
1832 #[test]
1833 fn test_entity_error_equality() {
1834 assert_eq!(GoudError::EntityNotFound, GoudError::EntityNotFound);
1836 assert_eq!(GoudError::ComponentNotFound, GoudError::ComponentNotFound);
1837
1838 assert_ne!(GoudError::EntityNotFound, GoudError::EntityAlreadyExists);
1840 assert_ne!(
1841 GoudError::ComponentNotFound,
1842 GoudError::ComponentAlreadyExists
1843 );
1844
1845 let err1 = GoudError::QueryFailed("error".to_string());
1847 let err2 = GoudError::QueryFailed("error".to_string());
1848 assert_eq!(err1, err2);
1849
1850 let err3 = GoudError::QueryFailed("different".to_string());
1852 assert_ne!(err1, err3);
1853 }
1854
1855 #[test]
1856 fn test_entity_error_debug_format() {
1857 let error = GoudError::EntityNotFound;
1858 let debug_str = format!("{:?}", error);
1859 assert!(debug_str.contains("EntityNotFound"));
1860
1861 let error2 = GoudError::QueryFailed("access conflict".to_string());
1862 let debug_str2 = format!("{:?}", error2);
1863 assert!(debug_str2.contains("QueryFailed"));
1864 assert!(debug_str2.contains("access conflict"));
1865 }
1866
1867 #[test]
1868 fn test_entity_error_codes_are_distinct() {
1869 let codes = vec![
1870 ERR_ENTITY_NOT_FOUND,
1871 ERR_ENTITY_ALREADY_EXISTS,
1872 ERR_COMPONENT_NOT_FOUND,
1873 ERR_COMPONENT_ALREADY_EXISTS,
1874 ERR_QUERY_FAILED,
1875 ];
1876
1877 for (i, code1) in codes.iter().enumerate() {
1878 for (j, code2) in codes.iter().enumerate() {
1879 if i != j {
1880 assert_ne!(
1881 code1, code2,
1882 "Error codes at index {} and {} should be different",
1883 i, j
1884 );
1885 }
1886 }
1887 }
1888 }
1889
1890 #[test]
1891 fn test_entity_error_code_gaps_for_future_expansion() {
1892 assert!(ERR_ENTITY_NOT_FOUND == 300);
1894 assert!(ERR_ENTITY_ALREADY_EXISTS == 301);
1895
1896 assert!(ERR_COMPONENT_NOT_FOUND == 310);
1898 assert!(ERR_COMPONENT_ALREADY_EXISTS == 311);
1899
1900 assert!(ERR_QUERY_FAILED == 320);
1902 }
1903 }
1904
1905 mod system_errors {
1910 use super::*;
1911
1912 #[test]
1913 fn test_window_creation_failed_error_code() {
1914 let error = GoudError::WindowCreationFailed("No display server found".to_string());
1915 assert_eq!(error.error_code(), ERR_WINDOW_CREATION_FAILED);
1916 assert_eq!(error.error_code(), 500);
1917 }
1918
1919 #[test]
1920 fn test_audio_init_failed_error_code() {
1921 let error = GoudError::AudioInitFailed("No audio devices found".to_string());
1922 assert_eq!(error.error_code(), ERR_AUDIO_INIT_FAILED);
1923 assert_eq!(error.error_code(), 510);
1924 }
1925
1926 #[test]
1927 fn test_physics_init_failed_error_code() {
1928 let error = GoudError::PhysicsInitFailed("Invalid gravity configuration".to_string());
1929 assert_eq!(error.error_code(), ERR_PHYSICS_INIT_FAILED);
1930 assert_eq!(error.error_code(), 520);
1931 }
1932
1933 #[test]
1934 fn test_platform_error_error_code() {
1935 let error =
1936 GoudError::PlatformError("macOS: Failed to acquire Metal device".to_string());
1937 assert_eq!(error.error_code(), ERR_PLATFORM_ERROR);
1938 assert_eq!(error.error_code(), 530);
1939 }
1940
1941 #[test]
1942 fn test_all_system_errors_in_system_category() {
1943 let errors: Vec<GoudError> = vec![
1944 GoudError::WindowCreationFailed("test".to_string()),
1945 GoudError::AudioInitFailed("test".to_string()),
1946 GoudError::PhysicsInitFailed("test".to_string()),
1947 GoudError::PlatformError("test".to_string()),
1948 ];
1949
1950 for error in errors {
1951 assert_eq!(
1952 error.category(),
1953 "System",
1954 "Error {:?} should be in System category",
1955 error
1956 );
1957 }
1958 }
1959
1960 #[test]
1961 fn test_system_error_codes_in_valid_range() {
1962 let errors: Vec<GoudError> = vec![
1963 GoudError::WindowCreationFailed("test".to_string()),
1964 GoudError::AudioInitFailed("test".to_string()),
1965 GoudError::PhysicsInitFailed("test".to_string()),
1966 GoudError::PlatformError("test".to_string()),
1967 ];
1968
1969 for error in errors {
1970 let code = error.error_code();
1971 assert!(
1972 code >= 500 && code < 600,
1973 "System error {:?} has code {} which is outside range 500-599",
1974 error,
1975 code
1976 );
1977 }
1978 }
1979
1980 #[test]
1981 fn test_system_errors_preserve_message() {
1982 let window_err = "Failed to create GLFW window: 800x600";
1984 if let GoudError::WindowCreationFailed(msg) =
1985 GoudError::WindowCreationFailed(window_err.to_string())
1986 {
1987 assert_eq!(msg, window_err);
1988 } else {
1989 panic!("Expected WindowCreationFailed variant");
1990 }
1991
1992 let audio_err = "ALSA: Unable to open default audio device";
1994 if let GoudError::AudioInitFailed(msg) =
1995 GoudError::AudioInitFailed(audio_err.to_string())
1996 {
1997 assert_eq!(msg, audio_err);
1998 } else {
1999 panic!("Expected AudioInitFailed variant");
2000 }
2001
2002 let physics_err = "Box2D: Invalid world bounds";
2004 if let GoudError::PhysicsInitFailed(msg) =
2005 GoudError::PhysicsInitFailed(physics_err.to_string())
2006 {
2007 assert_eq!(msg, physics_err);
2008 } else {
2009 panic!("Expected PhysicsInitFailed variant");
2010 }
2011
2012 let platform_err = "Linux: X11 display connection failed";
2014 if let GoudError::PlatformError(msg) =
2015 GoudError::PlatformError(platform_err.to_string())
2016 {
2017 assert_eq!(msg, platform_err);
2018 } else {
2019 panic!("Expected PlatformError variant");
2020 }
2021 }
2022
2023 #[test]
2024 fn test_system_error_equality() {
2025 let err1 = GoudError::WindowCreationFailed("error".to_string());
2027 let err2 = GoudError::WindowCreationFailed("error".to_string());
2028 assert_eq!(err1, err2);
2029
2030 let err3 = GoudError::WindowCreationFailed("different".to_string());
2032 assert_ne!(err1, err3);
2033
2034 let err4 = GoudError::AudioInitFailed("error".to_string());
2036 assert_ne!(err1, err4);
2037 }
2038
2039 #[test]
2040 fn test_system_error_debug_format() {
2041 let error = GoudError::WindowCreationFailed("GLFW error 65543".to_string());
2042 let debug_str = format!("{:?}", error);
2043 assert!(debug_str.contains("WindowCreationFailed"));
2044 assert!(debug_str.contains("GLFW error 65543"));
2045
2046 let error2 = GoudError::PlatformError("Win32 error".to_string());
2047 let debug_str2 = format!("{:?}", error2);
2048 assert!(debug_str2.contains("PlatformError"));
2049 assert!(debug_str2.contains("Win32 error"));
2050 }
2051
2052 #[test]
2053 fn test_system_error_codes_are_distinct() {
2054 let codes = vec![
2055 ERR_WINDOW_CREATION_FAILED,
2056 ERR_AUDIO_INIT_FAILED,
2057 ERR_PHYSICS_INIT_FAILED,
2058 ERR_PLATFORM_ERROR,
2059 ];
2060
2061 for (i, code1) in codes.iter().enumerate() {
2062 for (j, code2) in codes.iter().enumerate() {
2063 if i != j {
2064 assert_ne!(
2065 code1, code2,
2066 "Error codes at index {} and {} should be different",
2067 i, j
2068 );
2069 }
2070 }
2071 }
2072 }
2073
2074 #[test]
2075 fn test_system_error_code_gaps_for_future_expansion() {
2076 assert!(ERR_WINDOW_CREATION_FAILED == 500);
2078
2079 assert!(ERR_AUDIO_INIT_FAILED == 510);
2081
2082 assert!(ERR_PHYSICS_INIT_FAILED == 520);
2084
2085 assert!(ERR_PLATFORM_ERROR == 530);
2087 }
2088 }
2089
2090 mod internal_errors {
2095 use super::*;
2096
2097 #[test]
2098 fn test_internal_error_error_code() {
2099 let error =
2100 GoudError::InternalError("Unexpected null pointer in render queue".to_string());
2101 assert_eq!(error.error_code(), ERR_INTERNAL_ERROR);
2102 assert_eq!(error.error_code(), 900);
2103 }
2104
2105 #[test]
2106 fn test_not_implemented_error_code() {
2107 let error = GoudError::NotImplemented("Vulkan backend".to_string());
2108 assert_eq!(error.error_code(), ERR_NOT_IMPLEMENTED);
2109 assert_eq!(error.error_code(), 901);
2110 }
2111
2112 #[test]
2113 fn test_invalid_state_error_code() {
2114 let error = GoudError::InvalidState("Renderer called after shutdown".to_string());
2115 assert_eq!(error.error_code(), ERR_INVALID_STATE);
2116 assert_eq!(error.error_code(), 902);
2117 }
2118
2119 #[test]
2120 fn test_all_internal_errors_in_internal_category() {
2121 let errors: Vec<GoudError> = vec![
2122 GoudError::InternalError("test".to_string()),
2123 GoudError::NotImplemented("test".to_string()),
2124 GoudError::InvalidState("test".to_string()),
2125 ];
2126
2127 for error in errors {
2128 assert_eq!(
2129 error.category(),
2130 "Internal",
2131 "Error {:?} should be in Internal category",
2132 error
2133 );
2134 }
2135 }
2136
2137 #[test]
2138 fn test_internal_error_codes_in_valid_range() {
2139 let errors: Vec<GoudError> = vec![
2140 GoudError::InternalError("test".to_string()),
2141 GoudError::NotImplemented("test".to_string()),
2142 GoudError::InvalidState("test".to_string()),
2143 ];
2144
2145 for error in errors {
2146 let code = error.error_code();
2147 assert!(
2148 code >= 900 && code < 1000,
2149 "Internal error {:?} has code {} which is outside range 900-999",
2150 error,
2151 code
2152 );
2153 }
2154 }
2155
2156 #[test]
2157 fn test_internal_errors_preserve_message() {
2158 let internal_err = "FATAL: Inconsistent component storage state";
2160 if let GoudError::InternalError(msg) =
2161 GoudError::InternalError(internal_err.to_string())
2162 {
2163 assert_eq!(msg, internal_err);
2164 } else {
2165 panic!("Expected InternalError variant");
2166 }
2167
2168 let not_impl_err = "Feature 'ray tracing' is not yet implemented";
2170 if let GoudError::NotImplemented(msg) =
2171 GoudError::NotImplemented(not_impl_err.to_string())
2172 {
2173 assert_eq!(msg, not_impl_err);
2174 } else {
2175 panic!("Expected NotImplemented variant");
2176 }
2177
2178 let invalid_state_err = "Cannot add components while iterating";
2180 if let GoudError::InvalidState(msg) =
2181 GoudError::InvalidState(invalid_state_err.to_string())
2182 {
2183 assert_eq!(msg, invalid_state_err);
2184 } else {
2185 panic!("Expected InvalidState variant");
2186 }
2187 }
2188
2189 #[test]
2190 fn test_internal_error_equality() {
2191 let err1 = GoudError::InternalError("bug".to_string());
2193 let err2 = GoudError::InternalError("bug".to_string());
2194 assert_eq!(err1, err2);
2195
2196 let err3 = GoudError::InternalError("different bug".to_string());
2198 assert_ne!(err1, err3);
2199
2200 let err4 = GoudError::NotImplemented("bug".to_string());
2202 assert_ne!(err1, err4);
2203
2204 let err5 = GoudError::InvalidState("bug".to_string());
2205 assert_ne!(err1, err5);
2206 assert_ne!(err4, err5);
2207 }
2208
2209 #[test]
2210 fn test_internal_error_debug_format() {
2211 let error = GoudError::InternalError("assertion failed".to_string());
2212 let debug_str = format!("{:?}", error);
2213 assert!(debug_str.contains("InternalError"));
2214 assert!(debug_str.contains("assertion failed"));
2215
2216 let error2 = GoudError::NotImplemented("3D audio".to_string());
2217 let debug_str2 = format!("{:?}", error2);
2218 assert!(debug_str2.contains("NotImplemented"));
2219 assert!(debug_str2.contains("3D audio"));
2220
2221 let error3 = GoudError::InvalidState("already running".to_string());
2222 let debug_str3 = format!("{:?}", error3);
2223 assert!(debug_str3.contains("InvalidState"));
2224 assert!(debug_str3.contains("already running"));
2225 }
2226
2227 #[test]
2228 fn test_internal_error_codes_are_distinct() {
2229 let codes = vec![ERR_INTERNAL_ERROR, ERR_NOT_IMPLEMENTED, ERR_INVALID_STATE];
2230
2231 for (i, code1) in codes.iter().enumerate() {
2232 for (j, code2) in codes.iter().enumerate() {
2233 if i != j {
2234 assert_ne!(
2235 code1, code2,
2236 "Error codes at index {} and {} should be different",
2237 i, j
2238 );
2239 }
2240 }
2241 }
2242 }
2243
2244 #[test]
2245 fn test_internal_error_code_ordering() {
2246 assert_eq!(ERR_INTERNAL_ERROR, 900);
2248 assert_eq!(ERR_NOT_IMPLEMENTED, 901);
2249 assert_eq!(ERR_INVALID_STATE, 902);
2250 }
2251 }
2252
2253 mod traits {
2258 use super::*;
2259 use std::error::Error;
2260
2261 #[test]
2262 fn test_display_format_context_errors() {
2263 let error = GoudError::NotInitialized;
2264 let display = format!("{}", error);
2265 assert_eq!(display, "[GOUD-1] Context: Engine has not been initialized");
2266
2267 let error = GoudError::InitializationFailed("GPU not found".to_string());
2268 let display = format!("{}", error);
2269 assert_eq!(display, "[GOUD-10] Context: GPU not found");
2270 }
2271
2272 #[test]
2273 fn test_display_format_resource_errors() {
2274 let error = GoudError::ResourceNotFound("textures/player.png".to_string());
2275 let display = format!("{}", error);
2276 assert_eq!(display, "[GOUD-100] Resource: textures/player.png");
2277
2278 let error = GoudError::InvalidHandle;
2279 let display = format!("{}", error);
2280 assert_eq!(display, "[GOUD-110] Resource: Invalid handle");
2281 }
2282
2283 #[test]
2284 fn test_display_format_graphics_errors() {
2285 let error = GoudError::ShaderCompilationFailed("syntax error at line 42".to_string());
2286 let display = format!("{}", error);
2287 assert_eq!(display, "[GOUD-200] Graphics: syntax error at line 42");
2288 }
2289
2290 #[test]
2291 fn test_display_format_entity_errors() {
2292 let error = GoudError::EntityNotFound;
2293 let display = format!("{}", error);
2294 assert_eq!(display, "[GOUD-300] Entity: Entity not found");
2295
2296 let error = GoudError::QueryFailed("conflicting access".to_string());
2297 let display = format!("{}", error);
2298 assert_eq!(display, "[GOUD-320] Entity: conflicting access");
2299 }
2300
2301 #[test]
2302 fn test_display_format_system_errors() {
2303 let error = GoudError::WindowCreationFailed("no display".to_string());
2304 let display = format!("{}", error);
2305 assert_eq!(display, "[GOUD-500] System: no display");
2306 }
2307
2308 #[test]
2309 fn test_display_format_internal_errors() {
2310 let error = GoudError::InternalError("unexpected state".to_string());
2311 let display = format!("{}", error);
2312 assert_eq!(display, "[GOUD-900] Internal: unexpected state");
2313
2314 let error = GoudError::NotImplemented("feature X".to_string());
2315 let display = format!("{}", error);
2316 assert_eq!(display, "[GOUD-901] Internal: feature X");
2317 }
2318
2319 #[test]
2320 fn test_error_trait_implementation() {
2321 let error = GoudError::NotInitialized;
2322
2323 let error_ref: &dyn Error = &error;
2325 assert!(error_ref.source().is_none());
2326
2327 let display = format!("{}", error_ref);
2329 assert!(display.contains("GOUD-1"));
2330 }
2331
2332 #[test]
2333 fn test_message_method() {
2334 assert_eq!(
2336 GoudError::NotInitialized.message(),
2337 "Engine has not been initialized"
2338 );
2339 assert_eq!(GoudError::InvalidHandle.message(), "Invalid handle");
2340 assert_eq!(GoudError::EntityNotFound.message(), "Entity not found");
2341
2342 let error = GoudError::InitializationFailed("custom message".to_string());
2344 assert_eq!(error.message(), "custom message");
2345
2346 let error = GoudError::ResourceNotFound("path/to/file".to_string());
2347 assert_eq!(error.message(), "path/to/file");
2348 }
2349
2350 #[test]
2351 fn test_from_io_error_not_found() {
2352 let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
2353 let goud_error: GoudError = io_error.into();
2354
2355 assert!(matches!(goud_error, GoudError::ResourceNotFound(_)));
2356 assert_eq!(goud_error.error_code(), ERR_RESOURCE_NOT_FOUND);
2357 }
2358
2359 #[test]
2360 fn test_from_io_error_permission_denied() {
2361 let io_error =
2362 std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
2363 let goud_error: GoudError = io_error.into();
2364
2365 assert!(matches!(goud_error, GoudError::ResourceLoadFailed(_)));
2366 assert_eq!(goud_error.error_code(), ERR_RESOURCE_LOAD_FAILED);
2367 assert!(goud_error.message().contains("Permission denied"));
2368 }
2369
2370 #[test]
2371 fn test_from_io_error_other() {
2372 let io_error =
2373 std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "network error");
2374 let goud_error: GoudError = io_error.into();
2375
2376 assert!(matches!(goud_error, GoudError::ResourceLoadFailed(_)));
2377 assert_eq!(goud_error.error_code(), ERR_RESOURCE_LOAD_FAILED);
2378 }
2379
2380 #[test]
2381 fn test_from_string() {
2382 let msg = "something went wrong".to_string();
2383 let error: GoudError = msg.into();
2384
2385 assert!(matches!(error, GoudError::InternalError(_)));
2386 assert_eq!(error.message(), "something went wrong");
2387 assert_eq!(error.error_code(), ERR_INTERNAL_ERROR);
2388 }
2389
2390 #[test]
2391 fn test_from_str() {
2392 let error: GoudError = "oops".into();
2393
2394 assert!(matches!(error, GoudError::InternalError(_)));
2395 assert_eq!(error.message(), "oops");
2396 assert_eq!(error.error_code(), ERR_INTERNAL_ERROR);
2397 }
2398
2399 #[test]
2400 fn test_goud_result_ok() {
2401 fn might_fail() -> GoudResult<i32> {
2402 Ok(42)
2403 }
2404
2405 let result = might_fail();
2406 assert!(result.is_ok());
2407 assert_eq!(result.unwrap(), 42);
2408 }
2409
2410 #[test]
2411 fn test_goud_result_err() {
2412 fn always_fails() -> GoudResult<i32> {
2413 Err(GoudError::NotInitialized)
2414 }
2415
2416 let result = always_fails();
2417 assert!(result.is_err());
2418 let error = result.unwrap_err();
2419 assert_eq!(error, GoudError::NotInitialized);
2420 }
2421
2422 #[test]
2423 fn test_goud_result_with_question_mark() {
2424 fn inner() -> GoudResult<i32> {
2425 Err(GoudError::ResourceNotFound("missing.txt".to_string()))
2426 }
2427
2428 fn outer() -> GoudResult<i32> {
2429 let value = inner()?;
2430 Ok(value + 1)
2431 }
2432
2433 let result = outer();
2434 assert!(result.is_err());
2435 if let Err(GoudError::ResourceNotFound(msg)) = result {
2436 assert_eq!(msg, "missing.txt");
2437 } else {
2438 panic!("Expected ResourceNotFound error");
2439 }
2440 }
2441
2442 #[test]
2443 fn test_error_can_be_boxed() {
2444 let error: Box<dyn Error> = Box::new(GoudError::NotInitialized);
2446 let display = format!("{}", error);
2447 assert!(display.contains("GOUD-1"));
2448 }
2449
2450 #[test]
2451 fn test_display_versus_debug() {
2452 let error = GoudError::InitializationFailed("test message".to_string());
2453
2454 let display = format!("{}", error);
2455 let debug = format!("{:?}", error);
2456
2457 assert!(display.contains("[GOUD-10]"));
2459 assert!(display.contains("Context"));
2460 assert!(display.contains("test message"));
2461
2462 assert!(debug.contains("InitializationFailed"));
2464 assert!(debug.contains("test message"));
2465
2466 assert_ne!(display, debug);
2468 }
2469 }
2470
2471 mod ffi {
2476 use super::*;
2477 use std::sync::{Arc, Barrier};
2478 use std::thread;
2479
2480 fn with_clean_error_state<F, R>(f: F) -> R
2482 where
2483 F: FnOnce() -> R,
2484 {
2485 clear_last_error();
2486 let result = f();
2487 clear_last_error();
2488 result
2489 }
2490
2491 #[test]
2492 fn test_set_and_get_last_error() {
2493 with_clean_error_state(|| {
2494 set_last_error(GoudError::NotInitialized);
2495 let error = get_last_error();
2496 assert!(error.is_some());
2497 assert_eq!(error.unwrap(), GoudError::NotInitialized);
2498 });
2499 }
2500
2501 #[test]
2502 fn test_get_does_not_clear_error() {
2503 with_clean_error_state(|| {
2504 set_last_error(GoudError::NotInitialized);
2505
2506 let error1 = get_last_error();
2508 let error2 = get_last_error();
2509 let error3 = get_last_error();
2510
2511 assert_eq!(error1, error2);
2513 assert_eq!(error2, error3);
2514 assert!(error1.is_some());
2515 });
2516 }
2517
2518 #[test]
2519 fn test_take_clears_error() {
2520 with_clean_error_state(|| {
2521 set_last_error(GoudError::NotInitialized);
2522
2523 let error1 = take_last_error();
2525 assert!(error1.is_some());
2526 assert_eq!(error1.unwrap(), GoudError::NotInitialized);
2527
2528 let error2 = take_last_error();
2530 assert!(error2.is_none());
2531
2532 let error3 = take_last_error();
2533 assert!(error3.is_none());
2534 });
2535 }
2536
2537 #[test]
2538 fn test_last_error_code_no_error() {
2539 with_clean_error_state(|| {
2540 assert_eq!(last_error_code(), SUCCESS);
2542 });
2543 }
2544
2545 #[test]
2546 fn test_last_error_code_with_error() {
2547 with_clean_error_state(|| {
2548 set_last_error(GoudError::NotInitialized);
2549 assert_eq!(last_error_code(), ERR_NOT_INITIALIZED);
2550
2551 set_last_error(GoudError::ResourceNotFound("test".to_string()));
2552 assert_eq!(last_error_code(), ERR_RESOURCE_NOT_FOUND);
2553
2554 set_last_error(GoudError::ShaderCompilationFailed("error".to_string()));
2555 assert_eq!(last_error_code(), ERR_SHADER_COMPILATION_FAILED);
2556 });
2557 }
2558
2559 #[test]
2560 fn test_last_error_message_no_error() {
2561 with_clean_error_state(|| {
2562 assert!(last_error_message().is_none());
2564 });
2565 }
2566
2567 #[test]
2568 fn test_last_error_message_with_error() {
2569 with_clean_error_state(|| {
2570 set_last_error(GoudError::NotInitialized);
2572 let msg = last_error_message();
2573 assert!(msg.is_some());
2574 assert_eq!(msg.unwrap(), "Engine has not been initialized");
2575
2576 set_last_error(GoudError::InitializationFailed("GPU not found".to_string()));
2578 let msg = last_error_message();
2579 assert!(msg.is_some());
2580 assert_eq!(msg.unwrap(), "GPU not found");
2581 });
2582 }
2583
2584 #[test]
2585 fn test_clear_last_error() {
2586 with_clean_error_state(|| {
2587 set_last_error(GoudError::NotInitialized);
2588 assert_eq!(last_error_code(), ERR_NOT_INITIALIZED);
2589
2590 clear_last_error();
2591
2592 assert_eq!(last_error_code(), SUCCESS);
2593 assert!(last_error_message().is_none());
2594 assert!(get_last_error().is_none());
2595 });
2596 }
2597
2598 #[test]
2599 fn test_overwrite_error() {
2600 with_clean_error_state(|| {
2601 set_last_error(GoudError::NotInitialized);
2603 assert_eq!(last_error_code(), ERR_NOT_INITIALIZED);
2604
2605 set_last_error(GoudError::ResourceNotFound("file.txt".to_string()));
2607 assert_eq!(last_error_code(), ERR_RESOURCE_NOT_FOUND);
2608
2609 let error = take_last_error();
2611 assert!(matches!(error, Some(GoudError::ResourceNotFound(_))));
2612 });
2613 }
2614
2615 #[test]
2616 fn test_thread_isolation() {
2617 let barrier = Arc::new(Barrier::new(2));
2619 let barrier_clone = Arc::clone(&barrier);
2620
2621 let handle = thread::spawn(move || {
2622 clear_last_error();
2624
2625 barrier_clone.wait();
2627
2628 assert_eq!(
2630 last_error_code(),
2631 SUCCESS,
2632 "Thread should have no error from main thread"
2633 );
2634
2635 set_last_error(GoudError::ResourceNotFound("thread_file.txt".to_string()));
2637 assert_eq!(last_error_code(), ERR_RESOURCE_NOT_FOUND);
2638
2639 barrier_clone.wait();
2641 });
2642
2643 with_clean_error_state(|| {
2644 set_last_error(GoudError::NotInitialized);
2646 assert_eq!(last_error_code(), ERR_NOT_INITIALIZED);
2647
2648 barrier.wait();
2650
2651 barrier.wait();
2653
2654 assert_eq!(
2656 last_error_code(),
2657 ERR_NOT_INITIALIZED,
2658 "Main thread error should not be affected by spawned thread"
2659 );
2660 });
2661
2662 handle.join().unwrap();
2663 }
2664
2665 #[test]
2666 fn test_multiple_threads_independent_errors() {
2667 use std::sync::atomic::{AtomicUsize, Ordering};
2668
2669 const THREAD_COUNT: usize = 4;
2670 let success_count = Arc::new(AtomicUsize::new(0));
2671
2672 let handles: Vec<_> = (0..THREAD_COUNT)
2673 .map(|i| {
2674 let success_count = Arc::clone(&success_count);
2675 thread::spawn(move || {
2676 clear_last_error();
2677
2678 let error = match i {
2680 0 => GoudError::NotInitialized,
2681 1 => GoudError::AlreadyInitialized,
2682 2 => GoudError::InvalidContext,
2683 _ => GoudError::ContextDestroyed,
2684 };
2685 let expected_code = error.error_code();
2686 set_last_error(error);
2687
2688 if last_error_code() == expected_code {
2690 success_count.fetch_add(1, Ordering::SeqCst);
2691 }
2692
2693 clear_last_error();
2694 })
2695 })
2696 .collect();
2697
2698 for handle in handles {
2699 handle.join().unwrap();
2700 }
2701
2702 assert_eq!(
2703 success_count.load(Ordering::SeqCst),
2704 THREAD_COUNT,
2705 "All threads should have set and verified their own errors"
2706 );
2707 }
2708
2709 #[test]
2714 fn test_ffi_result_success() {
2715 let result = GoudFFIResult::success();
2716 assert!(result.success);
2717 assert!(result.is_success());
2718 assert!(!result.is_error());
2719 assert_eq!(result.code, SUCCESS);
2720 }
2721
2722 #[test]
2723 fn test_ffi_result_from_code_success() {
2724 let result = GoudFFIResult::from_code(SUCCESS);
2725 assert!(result.success);
2726 assert_eq!(result.code, SUCCESS);
2727 }
2728
2729 #[test]
2730 fn test_ffi_result_from_code_error() {
2731 let result = GoudFFIResult::from_code(ERR_NOT_INITIALIZED);
2732 assert!(!result.success);
2733 assert!(result.is_error());
2734 assert!(!result.is_success());
2735 assert_eq!(result.code, ERR_NOT_INITIALIZED);
2736 }
2737
2738 #[test]
2739 fn test_ffi_result_from_error() {
2740 with_clean_error_state(|| {
2741 let result = GoudFFIResult::from_error(GoudError::NotInitialized);
2742 assert!(!result.success);
2743 assert_eq!(result.code, ERR_NOT_INITIALIZED);
2744
2745 assert_eq!(last_error_code(), ERR_NOT_INITIALIZED);
2747 });
2748 }
2749
2750 #[test]
2751 fn test_ffi_result_from_error_with_message() {
2752 with_clean_error_state(|| {
2753 let error = GoudError::InitializationFailed("Custom error message".to_string());
2754 let result = GoudFFIResult::from_error(error);
2755
2756 assert!(!result.success);
2757 assert_eq!(result.code, ERR_INITIALIZATION_FAILED);
2758
2759 let msg = last_error_message();
2761 assert_eq!(msg, Some("Custom error message".to_string()));
2762 });
2763 }
2764
2765 #[test]
2766 fn test_ffi_result_from_result_ok() {
2767 with_clean_error_state(|| {
2768 set_last_error(GoudError::NotInitialized);
2770
2771 let result: GoudResult<i32> = Ok(42);
2772 let ffi_result = GoudFFIResult::from_result(result);
2773
2774 assert!(ffi_result.success);
2775 assert_eq!(ffi_result.code, SUCCESS);
2776
2777 assert_eq!(last_error_code(), SUCCESS);
2779 });
2780 }
2781
2782 #[test]
2783 fn test_ffi_result_from_result_err() {
2784 with_clean_error_state(|| {
2785 let result: GoudResult<i32> =
2786 Err(GoudError::ResourceNotFound("test.png".to_string()));
2787 let ffi_result = GoudFFIResult::from_result(result);
2788
2789 assert!(!ffi_result.success);
2790 assert_eq!(ffi_result.code, ERR_RESOURCE_NOT_FOUND);
2791
2792 assert_eq!(last_error_code(), ERR_RESOURCE_NOT_FOUND);
2794 assert_eq!(last_error_message(), Some("test.png".to_string()));
2795 });
2796 }
2797
2798 #[test]
2799 fn test_ffi_result_default() {
2800 let result = GoudFFIResult::default();
2801 assert!(result.success);
2802 assert_eq!(result.code, SUCCESS);
2803 }
2804
2805 #[test]
2806 fn test_ffi_result_from_goud_error() {
2807 with_clean_error_state(|| {
2808 let ffi_result: GoudFFIResult = GoudError::EntityNotFound.into();
2809 assert!(!ffi_result.success);
2810 assert_eq!(ffi_result.code, ERR_ENTITY_NOT_FOUND);
2811 });
2812 }
2813
2814 #[test]
2815 fn test_ffi_result_from_goud_result() {
2816 with_clean_error_state(|| {
2817 let ok: GoudResult<String> = Ok("hello".to_string());
2818 let ffi_result: GoudFFIResult = ok.into();
2819 assert!(ffi_result.success);
2820
2821 let err: GoudResult<String> = Err(GoudError::InvalidHandle);
2822 let ffi_result: GoudFFIResult = err.into();
2823 assert!(!ffi_result.success);
2824 assert_eq!(ffi_result.code, ERR_INVALID_HANDLE);
2825 });
2826 }
2827
2828 #[test]
2829 fn test_ffi_result_derive_traits() {
2830 let result1 = GoudFFIResult::from_code(ERR_NOT_INITIALIZED);
2832 let result2 = result1;
2833 assert_eq!(result1, result2);
2834
2835 let debug_str = format!("{:?}", result1);
2839 assert!(debug_str.contains("GoudFFIResult"));
2840 assert!(debug_str.contains("code"));
2841 assert!(debug_str.contains("success"));
2842
2843 assert_eq!(GoudFFIResult::success(), GoudFFIResult::success());
2845 assert_ne!(
2846 GoudFFIResult::success(),
2847 GoudFFIResult::from_code(ERR_NOT_INITIALIZED)
2848 );
2849 }
2850
2851 #[test]
2852 fn test_ffi_result_repr_c() {
2853 use std::mem::{align_of, size_of};
2855
2856 let size = size_of::<GoudFFIResult>();
2859 assert!(size >= 5, "GoudFFIResult should be at least 5 bytes");
2860 assert!(size <= 8, "GoudFFIResult should be at most 8 bytes");
2861
2862 let align = align_of::<GoudFFIResult>();
2864 assert!(
2865 align >= 4,
2866 "GoudFFIResult should have at least 4-byte alignment"
2867 );
2868 }
2869
2870 #[test]
2871 fn test_ffi_workflow_simulation() {
2872 with_clean_error_state(|| {
2879 fn rust_ffi_function() -> GoudFFIResult {
2881 let result: GoudResult<()> = Err(GoudError::ShaderCompilationFailed(
2882 "ERROR: 0:15: 'vec3' : undeclared identifier".to_string(),
2883 ));
2884 GoudFFIResult::from_result(result)
2885 }
2886
2887 let ffi_result = rust_ffi_function();
2889
2890 assert!(!ffi_result.success);
2892 assert_eq!(ffi_result.code, ERR_SHADER_COMPILATION_FAILED);
2893
2894 let code = last_error_code();
2896 let message = last_error_message();
2897
2898 assert_eq!(code, ERR_SHADER_COMPILATION_FAILED);
2899 assert!(message.is_some());
2900 assert!(message.unwrap().contains("vec3"));
2901
2902 clear_last_error();
2904
2905 assert_eq!(last_error_code(), SUCCESS);
2907 assert!(last_error_message().is_none());
2908 });
2909 }
2910
2911 #[test]
2912 fn test_ffi_success_workflow_simulation() {
2913 with_clean_error_state(|| {
2916 fn rust_ffi_function() -> GoudFFIResult {
2918 let result: GoudResult<()> = Ok(());
2919 GoudFFIResult::from_result(result)
2920 }
2921
2922 let ffi_result = rust_ffi_function();
2924
2925 assert!(ffi_result.success);
2927 assert_eq!(ffi_result.code, SUCCESS);
2928
2929 assert_eq!(last_error_code(), SUCCESS);
2931 assert!(last_error_message().is_none());
2932 });
2933 }
2934 }
2935}