matrei commented on code in PR #15067:
URL: https://github.com/apache/grails-core/pull/15067#discussion_r2372600421


##########
grails-geb/src/testFixtures/groovy/grails/plugin/geb/WebDriverContainerHolder.groovy:
##########
@@ -270,58 +367,117 @@ class WebDriverContainerHolder {
         Class<? extends ContainerFileDetector> fileDetector
 
         WebDriverContainerConfiguration(SpecInfo spec) {
-            ContainerGebConfiguration configuration
+
+            ContainerGebConfiguration conf
 
             // Check if the class implements the interface
             if (IContainerGebConfiguration.isAssignableFrom(spec.reflection)) {
-                configuration = spec.reflection.getConstructor().newInstance() 
as ContainerGebConfiguration
+                conf = spec.reflection.getConstructor().newInstance() as 
ContainerGebConfiguration
             } else {
                 // Check for the annotation
-                configuration = spec.annotations.find {
+                conf = spec.annotations.find {
                     it.annotationType() == ContainerGebConfiguration
                 } as ContainerGebConfiguration
             }
 
-            protocol = configuration?.protocol() ?: 
ContainerGebConfiguration.DEFAULT_PROTOCOL
-            hostName = configuration?.hostName() ?: 
ContainerGebConfiguration.DEFAULT_HOSTNAME_FROM_CONTAINER
-            reporting = configuration?.reporting() ?: false
-            fileDetector = configuration?.fileDetector() ?: 
ContainerGebConfiguration.DEFAULT_FILE_DETECTOR
+            protocol = conf?.protocol() ?: 
ContainerGebConfiguration.DEFAULT_PROTOCOL
+            hostName = conf?.hostName() ?: 
ContainerGebConfiguration.DEFAULT_HOSTNAME_FROM_CONTAINER
+            reporting = conf?.reporting() ?: false
+            fileDetector = conf?.fileDetector() ?: 
ContainerGebConfiguration.DEFAULT_FILE_DETECTOR
         }
     }
 
     /**
      * Workaround for 
https://github.com/testcontainers/testcontainers-java/issues/3998
-     * Restarts the VNC recording container to enable separate recording files 
for each test method.
-     * This method uses reflection to access the VNC recording container field 
in BrowserWebDriverContainer.
-     * Should be called BEFORE each test starts.
+     * <p>
+     * Restarts the VNC recording container to enable separate recording files 
for each
+     * test method. This method uses reflection to access the VNC recording 
container
+     * field in BrowserWebDriverContainer. Should be called BEFORE each test 
starts.
      */
     void restartVncRecordingContainer() {
-        if (!grailsGebSettings.recordingEnabled || 
!grailsGebSettings.restartRecordingContainerPerTest || !currentContainer) {
+        if (!settings.recordingEnabled || 
!settings.restartRecordingContainerPerTest || !container) {
             return
         }
         try {
             // Use reflection to access the VNC recording container field
-            Field vncRecordingContainerField = 
BrowserWebDriverContainer.getDeclaredField('vncRecordingContainer')
-            vncRecordingContainerField.setAccessible(true)
-
-            VncRecordingContainer vncContainer = 
vncRecordingContainerField.get(currentContainer) as VncRecordingContainer
+            def field = 
BrowserWebDriverContainer.getDeclaredField('vncRecordingContainer').tap {
+                accessible = true
+            }
 
+            def vncContainer = field.get(container) as VncRecordingContainer
             if (vncContainer) {
                 // Stop the current VNC recording container
                 vncContainer.stop()
                 // Create and start a new VNC recording container for the next 
test
-                VncRecordingContainer newVncContainer = new 
VncRecordingContainer(currentContainer)
+                def newVncContainer = new VncRecordingContainer(container)
                         .withVncPassword('secret')
                         .withVncPort(5900)
-                        .withVideoFormat(grailsGebSettings.recordingFormat)
-                vncRecordingContainerField.set(currentContainer, 
newVncContainer)
+                        .withVideoFormat(settings.recordingFormat)
+                field.set(container, newVncContainer)
                 newVncContainer.start()
 
                 log.debug('Successfully restarted VNC recording container')
             }
         } catch (Exception e) {
-            log.warn("Failed to restart VNC recording container: 
${e.message}", e)
+            log.warn("Failed to restart VNC recording container: $e.message", 
e)
             // Don't throw the exception to avoid breaking the test execution
         }
     }
+
+    @CompileStatic
+    private static class ClosureDecorators {
+
+        /**
+         * Wraps a closure so that during its execution, 
System.getProperty(key)
+         * returns a custom value instead of what is actually in the system 
properties.
+         */
+        static Closure withSystemProperty(Closure target, String key, Object 
value) {
+            Closure wrapped = { Object... args ->
+                SysPropScope.withProperty(key, value.toString()) {
+                    InvokerHelper.invokeClosure(target, args)
+                }
+            }
+
+            // keep original closure semantics
+            wrapped.rehydrate(target.delegate, target.owner, 
target.thisObject).tap {
+                resolveStrategy = target.resolveStrategy
+            }
+        }
+
+        @CompileStatic
+        private static class SysPropScope {
+
+            private static final ThreadLocal<Map<String,String>> 
OVERRIDDEN_SYSTEM_PROPERTIES =
+                    ThreadLocal.withInitial { [:] as Map<String,String> }
+
+            @Lazy // Thread-safe wrapping of system properties
+            private static Properties propertiesWrappedOnFirstAccess = {
+                new InterceptingProperties().tap {
+                    putAll(System.getProperties())
+                    System.setProperties(it)
+                }
+            }()
+
+            static <T> T withProperty(String key, String value, Closure<T> 
body) {
+                propertiesWrappedOnFirstAccess // Access property to trigger 
property wrapping
+                def map = OVERRIDDEN_SYSTEM_PROPERTIES.get()
+                def prev = map.put(key, value)
+                try {
+                    return body.call()
+                } finally {
+                    if (prev == null) map.remove(key) else map[key] = prev
+                    if (map.isEmpty()) OVERRIDDEN_SYSTEM_PROPERTIES.remove()

Review Comment:
   No, it should stay.
   ```groovy
   def c = ClosureDecorators.withSystemProperty({
       println System.getProperty('my.custom.property') // should print 
"customValue"
   }, 'my.custom.property', 'customValue')
   c()
   new Thread({
       sleep 1000
       c()
   }).start()
   println System.getProperty('my.custom.property') // should print original 
value, probably null
   ```
   ```console
   customValue
   null
   
   customValue
   ```
   
   Without it:
   ```console
   customValue
   customValue
   
   customValue
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to