T1 = Test Thread
T2 = JSF Thread
1 T1 - CleanUpPreviousContexts (T1 could be a T2 from a previous request, which is when it fails)
2 T1 - Execute the Test case
3 T1 - JSFSession -> Invoke JSF page
4 T2 - Create JSFUnitFacesContext(), associate with ThreadLocal T2
5 T2 - Execute JSF page
6 T2 - JSFUnitFacesContext.release¸ store FacesContext in shared HTTPSession
7 T1 - JSFSession/FacesContextBridge.getCurrentInstance, associate FacesContext from HTTPSession with this ThreadLocal T1
8 T1 - .. Do Test Stuff ..
9 T1 - Session.invalidate -> JSFUnitFacesContext.cleanUp, disassociate with ThreadLocal T1
In Step 7, JSFUnitFacesContext.setInstanceToJSFUnitThread() does not move the FacesContext association from T2 over to the T1, but rather associate the same FacesContext with both Threads. While in Step 9 the Session is invalidated and the JSFUnitFacesContext.cleanUp is called, it can only clean the T1 association, so the T2 association is left open. The application is then undeployed/redeployed and whom ever is so lucky to get the previous T2 thread as their T1 will fail.
So I think we need to disassociate with T2 in Step 6 after we store it in Session. The FacesContext is still open, but not useful in T2 anymore. FacesContext.release does this disassociation normally, but we override it to keep it alive for T1 to use.