13
13
import com .microsoft .dagx .spi .transfer .provision .ResourceManifestGenerator ;
14
14
import com .microsoft .dagx .spi .transfer .store .TransferProcessStore ;
15
15
import com .microsoft .dagx .spi .types .domain .transfer .*;
16
+ import com .microsoft .dagx .transfer .store .memory .InMemoryTransferProcessStore ;
16
17
import org .junit .jupiter .api .BeforeEach ;
17
18
import org .junit .jupiter .api .DisplayName ;
18
19
import org .junit .jupiter .api .Test ;
19
20
21
+ import java .util .ArrayList ;
20
22
import java .util .Collections ;
23
+ import java .util .UUID ;
21
24
import java .util .concurrent .CountDownLatch ;
22
25
import java .util .concurrent .TimeUnit ;
23
26
27
30
class TransferProcessManagerImplConsumerTest {
28
31
29
32
private static final long TIMEOUT = 5 ;
33
+ private final static int TRANSFER_MANAGER_BATCHSIZE = 10 ;
30
34
private TransferProcessManagerImpl transferProcessManager ;
31
35
private ProvisionManager provisionManager ;
32
36
private RemoteMessageDispatcherRegistry dispatcherRegistry ;
33
37
private StatusCheckerRegistry statusCheckerRegistry ;
38
+ private ExponentialWaitStrategy waitStrategyMock ;
34
39
35
40
@ BeforeEach
36
41
void setup () {
@@ -40,11 +45,17 @@ void setup() {
40
45
ResourceManifestGenerator manifestGenerator = mock (ResourceManifestGenerator .class );
41
46
42
47
statusCheckerRegistry = mock (StatusCheckerRegistry .class );
48
+
49
+ waitStrategyMock = partialMockBuilder (ExponentialWaitStrategy .class )
50
+ .withConstructor (1000L )
51
+ .addMockedMethod ("success" ).strictMock ();
52
+
53
+
43
54
transferProcessManager = TransferProcessManagerImpl .Builder .newInstance ()
44
55
.provisionManager (provisionManager )
45
56
.dataFlowManager (dataFlowManager )
46
- .waitStrategy (new ExponentialWaitStrategy ( 1000 ) )
47
- .batchSize (10 )
57
+ .waitStrategy (waitStrategyMock )
58
+ .batchSize (TRANSFER_MANAGER_BATCHSIZE )
48
59
.dispatcherRegistry (dispatcherRegistry )
49
60
.manifestGenerator (manifestGenerator )
50
61
.monitor (mock (Monitor .class ))
@@ -303,17 +314,68 @@ void verifyCompleted_noCheckerForSomeResources() throws InterruptedException {
303
314
assertThat (process .getState ()).describedAs ("State should be COMPLETED" ).isEqualTo (TransferProcessStates .COMPLETED .code ());
304
315
}
305
316
317
+ @ Test
318
+ @ DisplayName ("Verify that no process 'starves' during two consecutive runs, when the batch size > number of processes" )
319
+ void verifyProvision_shouldNotStarve () throws InterruptedException {
320
+ var numProcesses = TRANSFER_MANAGER_BATCHSIZE * 2 ;
321
+
322
+ //prepare process store
323
+ final TransferProcessStore inMemoryProcessStore = new InMemoryTransferProcessStore ();
324
+
325
+ //create a few processes
326
+ var processes = new ArrayList <TransferProcess >();
327
+ for (int i = 0 ; i < numProcesses ; i ++) {
328
+ final TransferProcess process = createTransferProcess (TransferProcessStates .UNSAVED );
329
+ processes .add (process );
330
+ inMemoryProcessStore .create (process );
331
+ }
332
+
333
+ var processesToProvision = new CountDownLatch (numProcesses ); //all processes should be provisioned
334
+
335
+ //prepare provision manager
336
+ provisionManager .provision (anyObject (TransferProcess .class ));
337
+ expectLastCall ().andAnswer (() -> {
338
+ processesToProvision .countDown ();
339
+ return null ;
340
+ }).anyTimes ();
341
+ replay (provisionManager );
342
+
343
+ // use the waitstrategy to count the number of iterations by making sure "success" was called exactly twice
344
+ waitStrategyMock .success ();
345
+ expectLastCall ().times (2 );
346
+ replay (waitStrategyMock );
347
+
348
+
349
+ //act
350
+ transferProcessManager .start (inMemoryProcessStore );
351
+
352
+ //assert
353
+ assertThat (processesToProvision .await (TIMEOUT , TimeUnit .SECONDS )).isTrue ();
354
+ verify (provisionManager );
355
+ verify (waitStrategyMock );
356
+ assertThat (processes ).describedAs ("All transfer processes should be in PROVISIONING state" ).allSatisfy (p -> {
357
+ var id = p .getId ();
358
+ var storedProcess = inMemoryProcessStore .find (id );
359
+ assertThat (storedProcess ).describedAs ("Should exist in the TransferProcessStore" ).isNotNull ();
360
+ assertThat (storedProcess .getState ()).isEqualTo (TransferProcessStates .PROVISIONING .code ());
361
+ });
362
+ }
363
+
306
364
private TransferProcess createTransferProcess (TransferProcessStates inState ) {
307
365
return createTransferProcess (inState , new TransferType ());
308
366
}
309
367
310
368
private TransferProcess createTransferProcess (TransferProcessStates inState , TransferType type ) {
369
+
370
+ String processId = UUID .randomUUID ().toString ();
371
+
311
372
final DataRequest mock = niceMock (DataRequest .class );
312
373
expect (mock .getTransferType ()).andReturn (type ).anyTimes ();
374
+ expect (mock .getId ()).andReturn (processId ).anyTimes ();
313
375
replay (mock );
314
376
return TransferProcess .Builder .newInstance ()
315
377
.state (inState .code ())
316
- .id ("test-process-id" )
378
+ .id ("test-process-" + processId )
317
379
.provisionedResourceSet (new ProvisionedResourceSet ())
318
380
.type (TransferProcess .Type .CLIENT )
319
381
.dataRequest (mock )
0 commit comments