001package arez; 002 003import java.lang.reflect.Field; 004import javax.annotation.Nonnull; 005import javax.annotation.Nullable; 006 007/** 008 * Utility class for interacting with Arez config settings in tests. 009 */ 010@SuppressWarnings( "WeakerAccess" ) 011@GwtIncompatible 012public final class ArezTestUtil 013{ 014 private ArezTestUtil() 015 { 016 } 017 018 /** 019 * Interface to intercept log messages emitted by Arez runtime. 020 */ 021 public interface Logger 022 { 023 void log( @Nonnull String message, @Nullable Throwable throwable ); 024 } 025 026 /** 027 * Reset the state of Arez config to either production or development state. 028 * 029 * @param productionMode true to set it to production mode configuration, false to set it to development mode config. 030 */ 031 public static void resetConfig( final boolean productionMode ) 032 { 033 if ( ArezConfig.isProductionEnvironment() ) 034 { 035 /* 036 * This should really never happen but if it does add assertion (so code stops in debugger) or 037 * failing that throw an exception. 038 */ 039 assert ArezConfig.isDevelopmentEnvironment(); 040 throw new IllegalStateException( "Unable to reset config as Arez is in production mode" ); 041 } 042 043 if ( productionMode ) 044 { 045 disableNames(); 046 disableVerify(); 047 disablePropertyIntrospectors(); 048 noEnforceTransactionType(); 049 disableSpies(); 050 makeCollectionPropertiesModifiable(); 051 disableNativeComponents(); 052 disableRegistries(); 053 noCheckInvariants(); 054 noCheckApiInvariants(); 055 } 056 else 057 { 058 enableNames(); 059 enableVerify(); 060 enablePropertyIntrospectors(); 061 enforceTransactionType(); 062 enableSpies(); 063 makeCollectionPropertiesUnmodifiable(); 064 enableNativeComponents(); 065 enableRegistries(); 066 checkInvariants(); 067 checkApiInvariants(); 068 } 069 enableObserverErrorHandlers(); 070 enableReferences(); 071 enableTaskInterceptor(); 072 purgeTasksWhenRunawayDetected(); 073 disableZones(); 074 resetState(); 075 } 076 077 /** 078 * Reset the state of Arez. 079 * This occasionally needs to be invoked after changing configuration settings in tests. 080 */ 081 private static void resetState() 082 { 083 setLogger( null ); 084 Transaction.setTransaction( null ); 085 ZoneHolder.reset(); 086 ArezContextHolder.reset(); 087 } 088 089 /** 090 * Specify logger to use to capture logging in tests 091 * 092 * @param logger the logger. 093 */ 094 public static void setLogger( @Nullable final Logger logger ) 095 { 096 if ( ArezConfig.isProductionEnvironment() ) 097 { 098 /* 099 * This should really never happen but if it does add assertion (so code stops in debugger) or 100 * failing that throw an exception. 101 */ 102 assert ArezConfig.isDevelopmentEnvironment(); 103 throw new IllegalStateException( "Unable to call ArezTestUtil.setLogger() as Arez is in production mode" ); 104 } 105 106 final ArezLogger.ProxyLogger proxyLogger = (ArezLogger.ProxyLogger) ArezLogger.getLogger(); 107 proxyLogger.setLogger( null == logger ? null : logger::log ); 108 } 109 110 /** 111 * Set the `arez.enable_names` setting to true. 112 */ 113 public static void enableNames() 114 { 115 setEnableNames( true ); 116 } 117 118 /** 119 * Set the `arez.enable_names` setting to false. 120 */ 121 public static void disableNames() 122 { 123 setEnableNames( false ); 124 } 125 126 /** 127 * Configure the `arez.enable_names` setting. 128 * 129 * @param value the setting. 130 */ 131 private static void setEnableNames( final boolean value ) 132 { 133 setConstant( "ENABLE_NAMES", value ); 134 } 135 136 /** 137 * Set the `arez.enable_references` setting to true. 138 */ 139 public static void enableReferences() 140 { 141 setEnableReferences( true ); 142 } 143 144 /** 145 * Set the `arez.enable_references` setting to false. 146 */ 147 public static void disableReferences() 148 { 149 setEnableReferences( false ); 150 } 151 152 /** 153 * Configure the `arez.enable_references` setting. 154 * 155 * @param value the setting. 156 */ 157 private static void setEnableReferences( final boolean value ) 158 { 159 setConstant( "ENABLE_REFERENCES", value ); 160 } 161 162 /** 163 * Set the `arez.enable_verify` setting to true. 164 */ 165 public static void enableVerify() 166 { 167 setEnableVerify( true ); 168 } 169 170 /** 171 * Set the `arez.enable_verify` setting to false. 172 */ 173 public static void disableVerify() 174 { 175 setEnableVerify( false ); 176 } 177 178 /** 179 * Configure the `arez.enable_verify` setting. 180 * 181 * @param value the setting. 182 */ 183 private static void setEnableVerify( final boolean value ) 184 { 185 setConstant( "ENABLE_VERIFY", value ); 186 } 187 188 /** 189 * Set the `arez.enable_property_introspection` setting to true. 190 */ 191 public static void enablePropertyIntrospectors() 192 { 193 setPropertyIntrospection( true ); 194 } 195 196 /** 197 * Set the `arez.enable_property_introspection` setting to false. 198 */ 199 public static void disablePropertyIntrospectors() 200 { 201 setPropertyIntrospection( false ); 202 } 203 204 /** 205 * Configure the `arez.enable_property_introspection` setting. 206 * 207 * @param value the setting. 208 */ 209 private static void setPropertyIntrospection( final boolean value ) 210 { 211 setConstant( "ENABLE_PROPERTY_INTROSPECTION", value ); 212 } 213 214 /** 215 * Set the `arez.purge_tasks_when_runaway_detected` setting to true. 216 */ 217 public static void purgeTasksWhenRunawayDetected() 218 { 219 setPurgeTasksWhenRunawayDetected( true ); 220 } 221 222 /** 223 * Set the `arez.purge_tasks_when_runaway_detected` setting to false. 224 */ 225 public static void noPurgeTasksWhenRunawayDetected() 226 { 227 setPurgeTasksWhenRunawayDetected( false ); 228 } 229 230 /** 231 * Configure the `arez.purge_tasks_when_runaway_detected` setting. 232 * 233 * @param value the setting. 234 */ 235 private static void setPurgeTasksWhenRunawayDetected( final boolean value ) 236 { 237 setConstant( "PURGE_ON_RUNAWAY", value ); 238 } 239 240 /** 241 * Set the `arez.enforce_transaction_type` setting to true. 242 */ 243 public static void enforceTransactionType() 244 { 245 setEnforceTransactionType( true ); 246 } 247 248 /** 249 * Set the `arez.enforce_transaction_type` setting to false. 250 */ 251 public static void noEnforceTransactionType() 252 { 253 setEnforceTransactionType( false ); 254 } 255 256 /** 257 * Configure the `arez.enforce_transaction_type` setting. 258 * 259 * @param value the setting. 260 */ 261 private static void setEnforceTransactionType( final boolean value ) 262 { 263 setConstant( "ENFORCE_TRANSACTION_TYPE", value ); 264 } 265 266 /** 267 * Set the `arez.enable_spies` setting to true. 268 */ 269 public static void enableSpies() 270 { 271 setEnableSpies( true ); 272 resetState(); 273 } 274 275 /** 276 * Set the `arez.enable_spies` setting to false. 277 */ 278 public static void disableSpies() 279 { 280 setEnableSpies( false ); 281 resetState(); 282 } 283 284 /** 285 * Configure the "arez.enable_spies" setting. 286 * 287 * @param value the setting. 288 */ 289 private static void setEnableSpies( final boolean value ) 290 { 291 setConstant( "ENABLE_SPIES", value ); 292 } 293 294 /** 295 * Set the `arez.enable_zones` setting to true. 296 * This will result in the Arez state being reset to align with this setting. The 297 * normal practice is to invoke this at the start of a test. 298 */ 299 public static void enableZones() 300 { 301 setEnableZones( true ); 302 resetState(); 303 } 304 305 /** 306 * Set the `arez.enable_zones` setting to false. 307 * This will result in the Arez state being reset to align with this setting. The 308 * normal practice is to invoke this at the start of a test. 309 */ 310 public static void disableZones() 311 { 312 setEnableZones( false ); 313 resetState(); 314 } 315 316 /** 317 * Configure the `arez.enable_zones` setting. 318 * 319 * @param value the setting. 320 */ 321 private static void setEnableZones( final boolean value ) 322 { 323 setConstant( "ENABLE_ZONES", value ); 324 } 325 326 /** 327 * Set the `arez.collections_properties_unmodifiable` setting to true. 328 */ 329 public static void makeCollectionPropertiesModifiable() 330 { 331 setCollectionPropertiesUnmodifiable( false ); 332 } 333 334 /** 335 * Set the `arez.collections_properties_unmodifiable` setting to false. 336 */ 337 public static void makeCollectionPropertiesUnmodifiable() 338 { 339 setCollectionPropertiesUnmodifiable( true ); 340 } 341 342 /** 343 * Configure the `arez.collections_properties_unmodifiable` setting. 344 * 345 * @param value the setting. 346 */ 347 private static void setCollectionPropertiesUnmodifiable( final boolean value ) 348 { 349 setConstant( "COLLECTION_PROPERTIES_UNMODIFIABLE", value ); 350 } 351 352 /** 353 * Set the `arez.enable_native_components` setting to true. 354 * This will result in the Arez state being reset to align with this setting. The 355 * normal practice is to invoke this at the start of a test. 356 */ 357 public static void enableNativeComponents() 358 { 359 setEnableNativeComponents( true ); 360 resetState(); 361 } 362 363 /** 364 * Set the `arez.enable_native_components` setting to false. 365 * This will result in the Arez state being reset to align with this setting. The 366 * normal practice is to invoke this at the start of a test. 367 */ 368 public static void disableNativeComponents() 369 { 370 setEnableNativeComponents( false ); 371 resetState(); 372 } 373 374 /** 375 * Configure the `arez.enable_native_components` setting. 376 * 377 * @param value the setting. 378 */ 379 private static void setEnableNativeComponents( final boolean value ) 380 { 381 setConstant( "ENABLE_NATIVE_COMPONENTS", value ); 382 } 383 384 /** 385 * Set the `arez.enable_registries` setting to true. 386 * This will result in the Arez state being reset to align with this setting. The 387 * normal practice is to invoke this at the start of a test. 388 */ 389 public static void enableRegistries() 390 { 391 setEnableRegistries( true ); 392 resetState(); 393 } 394 395 /** 396 * Set the `arez.enable_registries` setting to false. 397 * This will result in the Arez state being reset to align with this setting. The 398 * normal practice is to invoke this at the start of a test. 399 */ 400 public static void disableRegistries() 401 { 402 setEnableRegistries( false ); 403 resetState(); 404 } 405 406 /** 407 * Configure the `arez.enable_registries` setting. 408 * 409 * @param value the setting. 410 */ 411 private static void setEnableRegistries( final boolean value ) 412 { 413 setConstant( "ENABLE_REGISTRIES", value ); 414 } 415 416 /** 417 * Set the `arez.enable_task_interceptor` setting to true. 418 */ 419 public static void enableTaskInterceptor() 420 { 421 setEnableTaskInterceptor( true ); 422 resetState(); 423 } 424 425 /** 426 * Set the `arez.enable_task_interceptor` setting to false. 427 */ 428 public static void disableTaskInterceptor() 429 { 430 setEnableTaskInterceptor( false ); 431 resetState(); 432 } 433 434 /** 435 * Configure the "arez.enable_task_interceptor" setting. 436 * 437 * @param value the setting. 438 */ 439 private static void setEnableTaskInterceptor( final boolean value ) 440 { 441 setConstant( "ENABLE_TASK_INTERCEPTOR", value ); 442 } 443 444 /** 445 * Set the `arez.enable_observer_error_handlers` setting to true. 446 */ 447 public static void enableObserverErrorHandlers() 448 { 449 setEnableObserverErrorHandlers( true ); 450 } 451 452 /** 453 * Set the `arez.enable_observer_error_handlers` setting to false. 454 */ 455 public static void disableObserverErrorHandlers() 456 { 457 setEnableObserverErrorHandlers( false ); 458 } 459 460 /** 461 * Configure the `arez.enable_observer_error_handlers` setting. 462 * 463 * @param value the setting. 464 */ 465 private static void setEnableObserverErrorHandlers( final boolean value ) 466 { 467 setConstant( "ENABLE_OBSERVER_ERROR_HANDLERS", value ); 468 } 469 470 /** 471 * Set the `arez.check_invariants` setting to true. 472 */ 473 public static void checkInvariants() 474 { 475 setCheckInvariants( true ); 476 } 477 478 /** 479 * Set the `arez.check_invariants` setting to false. 480 */ 481 public static void noCheckInvariants() 482 { 483 setCheckInvariants( false ); 484 } 485 486 /** 487 * Configure the `arez.check_invariants` setting. 488 * 489 * @param checkInvariants the "check invariants" setting. 490 */ 491 private static void setCheckInvariants( final boolean checkInvariants ) 492 { 493 setConstant( "CHECK_INVARIANTS", checkInvariants ); 494 } 495 496 /** 497 * Set the `arez.check_api_invariants` setting to true. 498 */ 499 public static void checkApiInvariants() 500 { 501 setCheckApiInvariants( true ); 502 } 503 504 /** 505 * Set the `arez.check_api_invariants` setting to false. 506 */ 507 public static void noCheckApiInvariants() 508 { 509 setCheckApiInvariants( false ); 510 } 511 512 /** 513 * Configure the `arez.check_api_invariants` setting. 514 * 515 * @param checkApiInvariants the "check invariants" setting. 516 */ 517 private static void setCheckApiInvariants( final boolean checkApiInvariants ) 518 { 519 setConstant( "CHECK_API_INVARIANTS", checkApiInvariants ); 520 } 521 522 /** 523 * Set the specified field name on ArezConfig. 524 */ 525 @SuppressWarnings( "NonJREEmulationClassesInClientCode" ) 526 private static void setConstant( @Nonnull final String fieldName, final boolean value ) 527 { 528 if ( ArezConfig.isProductionEnvironment() ) 529 { 530 /* 531 * This should really never happen but if it does add assertion (so code stops in debugger) or 532 * failing that throw an exception. 533 */ 534 assert ArezConfig.isDevelopmentEnvironment(); 535 throw new IllegalStateException( "Unable to change constant " + fieldName + " as Arez is in production mode" ); 536 } 537 else 538 { 539 try 540 { 541 final Field field = ArezConfig.class.getDeclaredField( fieldName ); 542 field.setAccessible( true ); 543 field.set( null, value ); 544 } 545 catch ( final NoSuchFieldException | IllegalAccessException e ) 546 { 547 throw new IllegalStateException( "Unable to change constant " + fieldName, e ); 548 } 549 } 550 } 551}