@@ -1506,6 +1506,145 @@ func TestRetryReadStallEmulated(t *testing.T) {
1506
1506
}
1507
1507
}
1508
1508
1509
+ func TestWriterChunkTransferTimeoutEmulated (t * testing.T ) {
1510
+ transportClientTest (skipGRPC ("service is not implemented" ), t , func (t * testing.T , ctx context.Context , project , bucket string , client storageClient ) {
1511
+ _ , err := client .CreateBucket (ctx , project , bucket , & BucketAttrs {}, nil )
1512
+ if err != nil {
1513
+ t .Fatalf ("creating bucket: %v" , err )
1514
+ }
1515
+
1516
+ chunkSize := 2 * 1024 * 1024 // 2 MiB
1517
+ fileSize := 5 * 1024 * 1024 // 5 MiB
1518
+ tests := []struct {
1519
+ name string
1520
+ instructions map [string ][]string
1521
+ chunkTransferTimeout time.Duration
1522
+ expectedSuccess bool
1523
+ }{
1524
+ {
1525
+ name : "stall-on-first-chunk-with-chunk-transfer-timeout-zero" ,
1526
+ instructions : map [string ][]string {
1527
+ "storage.objects.insert" : {"stall-for-10s-after-1024K" },
1528
+ },
1529
+ chunkTransferTimeout : 0 ,
1530
+ expectedSuccess : false ,
1531
+ },
1532
+ {
1533
+ name : "stall-on-first-chunk-with-chunk-transfer-timeout-nonzero" ,
1534
+ instructions : map [string ][]string {
1535
+ "storage.objects.insert" : {"stall-for-10s-after-1024K" },
1536
+ },
1537
+ chunkTransferTimeout : 100 * time .Millisecond ,
1538
+ expectedSuccess : true ,
1539
+ },
1540
+ {
1541
+ name : "stall-on-second-chunk-with-chunk-transfer-timeout-zero" ,
1542
+ instructions : map [string ][]string {
1543
+ "storage.objects.insert" : {"stall-for-10s-after-3072K" },
1544
+ },
1545
+ chunkTransferTimeout : 0 ,
1546
+ expectedSuccess : false ,
1547
+ },
1548
+ {
1549
+ name : "stall-on-second-chunk-with-chunk-transfer-timeout-nonzero" ,
1550
+ instructions : map [string ][]string {
1551
+ "storage.objects.insert" : {"stall-for-10s-after-3072K" },
1552
+ },
1553
+ chunkTransferTimeout : 100 * time .Millisecond ,
1554
+ expectedSuccess : true ,
1555
+ },
1556
+ {
1557
+ name : "stall-on-first-chunk-twice-with-chunk-transfer-timeout-zero" ,
1558
+ instructions : map [string ][]string {
1559
+ "storage.objects.insert" : {"stall-for-10s-after-1024K" , "stall-for-10s-after-1024K" },
1560
+ },
1561
+ chunkTransferTimeout : 0 ,
1562
+ expectedSuccess : false ,
1563
+ },
1564
+ {
1565
+ name : "stall-on-first-chunk-twice-with-chunk-transfer-timeout-nonzero" ,
1566
+ instructions : map [string ][]string {
1567
+ "storage.objects.insert" : {"stall-for-10s-after-1024K" , "stall-for-10s-after-1024K" },
1568
+ },
1569
+ chunkTransferTimeout : 100 * time .Millisecond ,
1570
+ expectedSuccess : true ,
1571
+ },
1572
+ }
1573
+
1574
+ for _ , tc := range tests {
1575
+ t .Run (tc .name , func (t * testing.T ) {
1576
+ testID := createRetryTest (t , client , tc .instructions )
1577
+ var cancel context.CancelFunc
1578
+ rCtx := callctx .SetHeaders (ctx , "x-retry-test-id" , testID )
1579
+ rCtx , cancel = context .WithTimeout (rCtx , 1 * time .Second )
1580
+ defer cancel ()
1581
+
1582
+ prefix := time .Now ().Nanosecond ()
1583
+ want := & ObjectAttrs {
1584
+ Bucket : bucket ,
1585
+ Name : fmt .Sprintf ("%d-object-%d" , prefix , time .Now ().Nanosecond ()),
1586
+ Generation : defaultGen ,
1587
+ }
1588
+
1589
+ var gotAttrs * ObjectAttrs
1590
+ params := & openWriterParams {
1591
+ attrs : want ,
1592
+ bucket : bucket ,
1593
+ chunkSize : chunkSize ,
1594
+ chunkTransferTimeout : tc .chunkTransferTimeout ,
1595
+ ctx : rCtx ,
1596
+ donec : make (chan struct {}),
1597
+ setError : func (_ error ) {}, // no-op
1598
+ progress : func (_ int64 ) {}, // no-op
1599
+ setObj : func (o * ObjectAttrs ) { gotAttrs = o },
1600
+ }
1601
+
1602
+ pw , err := client .OpenWriter (params )
1603
+ if err != nil {
1604
+ t .Fatalf ("failed to open writer: %v" , err )
1605
+ }
1606
+ buffer := bytes .Repeat ([]byte ("A" ), fileSize )
1607
+ _ , err = pw .Write (buffer )
1608
+ if tc .expectedSuccess {
1609
+ if err != nil {
1610
+ t .Fatalf ("failed to populate test data: %v" , err )
1611
+ }
1612
+ if err := pw .Close (); err != nil {
1613
+ t .Fatalf ("closing object: %v" , err )
1614
+ }
1615
+ select {
1616
+ case <- params .donec :
1617
+ }
1618
+ if gotAttrs == nil {
1619
+ t .Fatalf ("Writer finished, but resulting object wasn't set" )
1620
+ }
1621
+ if diff := cmp .Diff (gotAttrs .Name , want .Name ); diff != "" {
1622
+ t .Fatalf ("Resulting object name: got(-),want(+):\n %s" , diff )
1623
+ }
1624
+
1625
+ r , err := veneerClient .Bucket (bucket ).Object (want .Name ).NewReader (ctx )
1626
+ if err != nil {
1627
+ t .Fatalf ("opening reading: %v" , err )
1628
+ }
1629
+ wantLen := len (buffer )
1630
+ got := make ([]byte , wantLen )
1631
+ n , err := r .Read (got )
1632
+ if n != wantLen {
1633
+ t .Fatalf ("expected to read %d bytes, but got %d" , wantLen , n )
1634
+ }
1635
+ if diff := cmp .Diff (got , buffer ); diff != "" {
1636
+ t .Fatalf ("checking written content: got(-),want(+):\n %s" , diff )
1637
+ }
1638
+ } else {
1639
+ if ! errors .Is (err , context .DeadlineExceeded ) {
1640
+ t .Fatalf ("expected context deadline exceeded found %v" , err )
1641
+ }
1642
+ }
1643
+ })
1644
+ }
1645
+ })
1646
+ }
1647
+
1509
1648
// createRetryTest creates a bucket in the emulator and sets up a test using the
1510
1649
// Retry Test API for the given instructions. This is intended for emulator tests
1511
1650
// of retry behavior that are not covered by conformance tests.
0 commit comments