@@ -4,6 +4,7 @@ import { createSerializer } from 'enzyme-to-json';
44import  {  configure  as  mobxConfigure  }  from  'mobx' ; 
55
66import  {  ElectronFiddleMock  }  from  './mocks/mocks' ; 
7+ import  {  getOrCreateMapValue  }  from  './utils' ; 
78
89enzymeConfigure ( {  adapter : new  Adapter ( )  } ) ; 
910
@@ -75,47 +76,159 @@ delete (window as any).localStorage;
7576window . navigator  =  window . navigator  ??  { } ; 
7677( window . navigator . clipboard  as  any )  =  { } ; 
7778
79+ type  MockLock  =  Lock  &  { 
80+   abortController : AbortController ; 
81+ } ; 
82+ 
7883class  FakeNavigatorLocks  implements  LockManager  { 
7984  locks  =  { 
80-     held : new  Set < Lock > ( ) , 
81-     pending : new  Set < Lock > ( ) , 
85+     held : new  Map < string ,  Map < LockMode ,  Set < MockLock > > > ( ) , 
8286  } ; 
8387
8488  query  =  async  ( )  =>  { 
8589    const  result  =  { 
86-       held : [ ...this . locks . held ] , 
87-       pending : [ ...this . locks . pending ] , 
90+       held : [ ...this . locks . held . values ( ) ] . reduce ( ( acc ,  item )  =>  { 
91+         acc . push ( ...[ ...item . get ( 'exclusive' ) ! . values ( ) ] ) ; 
92+         acc . push ( ...[ ...item . get ( 'shared' ) ! . values ( ) ] ) ; 
93+ 
94+         return  acc ; 
95+       } ,  [ ]  as  MockLock [ ] ) , 
8896    } ; 
8997
9098    return  result  as  LockManagerSnapshot ; 
9199  } ; 
92100
93-   /** 
94-    * WIP. Right now, this is a **very** naive mock that will just happily grant a shared lock when one is requested, 
95-    * but I'll add some bookkeeping and expand it to cover the exclusive lock case as well. 
96-    * 
97-    * @TODO  remove this comment 
98-    */ 
99101  request  =  ( async  ( ...args : Parameters < LockManager [ 'request' ] > )  =>  { 
100102    const  [ 
101103      name , 
102104      options  =  { 
105+         ifAvailable : false , 
103106        mode : 'exclusive' , 
107+         steal : false , 
104108      } , 
105109      cb , 
106110    ]  =  args ; 
107111
108-     const  {  mode }  =  options ; 
112+     const  {  ifAvailable,  mode,  steal }  =  options ; 
113+ 
114+     const  lock  =  { 
115+       name, 
116+       mode, 
117+       abortController : new  AbortController ( ) , 
118+     }  as  MockLock ; 
119+ 
120+     const  heldLocksWithSameName  =  getOrCreateMapValue ( 
121+       this . locks . held , 
122+       name , 
123+       new  Map < LockMode ,  Set < MockLock > > ( ) , 
124+     ) ; 
125+ 
126+     const  exclusiveLocksWithSameName  =  getOrCreateMapValue ( 
127+       heldLocksWithSameName , 
128+       'exclusive' , 
129+       new  Set < MockLock > ( ) , 
130+     ) ; 
109131
110-     const  lock  =  {  name,  mode,  cb }  as  Lock ; 
132+     const  sharedLocksWithSameName  =  getOrCreateMapValue ( 
133+       heldLocksWithSameName , 
134+       'shared' , 
135+       new  Set < MockLock > ( ) , 
136+     ) ; 
111137
112138    if  ( mode  ===  'shared' )  { 
113-       this . locks . held . add ( lock ) ; 
139+       sharedLocksWithSameName . add ( lock ) ; 
114140
115-       await  cb ( lock ) ; 
141+       try  { 
142+         await  cb ( lock ) ; 
143+       }  finally  { 
144+         sharedLocksWithSameName . delete ( lock ) ; 
145+       } 
146+ 
147+       return ; 
148+     } 
149+ 
150+     // exclusive lock 
151+ 
152+     // no locks with this name -> grant an exclusive lock 
153+     if  ( 
154+       exclusiveLocksWithSameName . size  ===  0  && 
155+       sharedLocksWithSameName . size  ===  0 
156+     )  { 
157+       exclusiveLocksWithSameName . add ( lock ) ; 
158+ 
159+       try  { 
160+         await  cb ( lock ) ; 
161+       }  finally  { 
162+         exclusiveLocksWithSameName . delete ( lock ) ; 
163+       } 
116164
117165      return ; 
118166    } 
167+ 
168+     // steal any currently held locks 
169+     if  ( steal )  { 
170+       for  ( const  lock  of  sharedLocksWithSameName )  { 
171+         lock . abortController . abort ( ) ; 
172+       } 
173+ 
174+       for  ( const  lock  of  exclusiveLocksWithSameName )  { 
175+         lock . abortController . abort ( ) ; 
176+       } 
177+ 
178+       sharedLocksWithSameName . clear ( ) ; 
179+       exclusiveLocksWithSameName . clear ( ) ; 
180+ 
181+       exclusiveLocksWithSameName . add ( lock ) ; 
182+ 
183+       try  { 
184+         await  cb ( lock ) ; 
185+       }  finally  { 
186+         exclusiveLocksWithSameName . delete ( lock ) ; 
187+       } 
188+ 
189+       return ; 
190+     } 
191+ 
192+     // run the callback without waiting for the lock to be released 
193+     if  ( ifAvailable )  { 
194+       // just run the callback without waiting for it 
195+       cb ( null ) ; 
196+ 
197+       return ; 
198+     } 
199+ 
200+     // @TODO  add the lock to the list of pending locks? 
201+ 
202+     // it's an exclusive lock, so there's only one value 
203+     const  currentLock  =  exclusiveLocksWithSameName . values ( ) . next ( ) 
204+       . value  as  MockLock ; 
205+ 
206+     const  {  abortController : currentLockAbortController  }  =  currentLock ; 
207+ 
208+     // wait for the current lock to be released 
209+     await  new  Promise < void > ( ( resolve ,  reject )  =>  { 
210+       currentLockAbortController . signal . onabort  =  ( )  =>  resolve ( ) ; 
211+ 
212+       const  {  abortController : pendingLockAbortController  }  =  lock ; 
213+ 
214+       // this allows the locking mechanism to release this lock 
215+       pendingLockAbortController . signal . onabort  =  ( )  =>  reject ( ) ; 
216+     } ) ; 
217+ 
218+     // clear the exclusive locks 
219+     exclusiveLocksWithSameName . clear ( ) ; 
220+ 
221+     // grant our lock 
222+     exclusiveLocksWithSameName . add ( lock ) ; 
223+ 
224+     try  { 
225+       // run the callback 
226+       await  cb ( lock ) ; 
227+     }  finally  { 
228+       exclusiveLocksWithSameName . delete ( lock ) ; 
229+     } 
230+ 
231+     return ; 
119232  } )  as  LockManager [ 'request' ] ; 
120233} 
121234
0 commit comments