@@ -254,3 +254,138 @@ func getTestCases(path string, shortOnly bool) ([]testCase, error) {
254254
255255 return ret , nil
256256}
257+
258+ // TestResetAfterSmallWrite verifies Reset() handles the case where Write()
259+ // was called (starting the addLayer goroutine) but no complete slabs were
260+ // processed. The goroutine must handle nil twinHold when receiving the
261+ // close signal.
262+ func TestResetAfterSmallWrite (t * testing.T ) {
263+ cp := & Calc {}
264+
265+ // Write a single byte - this initializes internal state and starts
266+ // the addLayer(0) goroutine, but data stays in buffer (not processed
267+ // into slabs since it's less than bufferSize)
268+ _ , err := cp .Write ([]byte {0x42 })
269+ if err != nil {
270+ t .Fatalf ("Write failed: %v" , err )
271+ }
272+
273+ // Reset() closes channels and waits for goroutine cleanup.
274+ // Must not panic even though no slabs were processed.
275+ cp .Reset ()
276+ }
277+
278+ // TestResetWithoutWrite verifies Reset() is safe on an uninitialized Calc.
279+ // Write(empty) is a no-op, so no goroutine is started.
280+ func TestResetWithoutWrite (t * testing.T ) {
281+ cp := & Calc {}
282+
283+ // Write zero bytes is a no-op
284+ _ , err := cp .Write ([]byte {})
285+ if err != nil {
286+ t .Fatalf ("Write failed: %v" , err )
287+ }
288+
289+ // Reset on never-initialized calc is safe
290+ cp .Reset ()
291+ }
292+
293+ // TestDigestErrorThenReset verifies that Reset() correctly cleans up after
294+ // Digest() returns an error due to insufficient data. When Digest() errors,
295+ // it does NOT close channels, so callers must call Reset() for cleanup.
296+ func TestDigestErrorThenReset (t * testing.T ) {
297+ cp := & Calc {}
298+
299+ // Write less than MinPiecePayload (65 bytes)
300+ data := make ([]byte , 23 )
301+ _ , err := cp .Write (data )
302+ if err != nil {
303+ t .Fatalf ("Write failed: %v" , err )
304+ }
305+
306+ // Digest returns error for insufficient data
307+ _ , _ , err = cp .Digest ()
308+ if err == nil {
309+ t .Fatal ("Expected error from Digest() with insufficient data" )
310+ }
311+
312+ // Reset cleans up goroutine - must not panic
313+ cp .Reset ()
314+ }
315+
316+ // TestDigestWithoutWrite verifies Digest() without any writes returns an
317+ // error and does not panic. No goroutine is started in this case.
318+ func TestDigestWithoutWrite (t * testing.T ) {
319+ cp := & Calc {}
320+
321+ // Digest with no data returns error
322+ _ , _ , err := cp .Digest ()
323+ if err == nil {
324+ t .Fatal ("Expected error from Digest() with no data" )
325+ }
326+
327+ // Reset on never-initialized calc is safe
328+ cp .Reset ()
329+ }
330+
331+ // TestMultipleResets verifies that calling Reset() multiple times is safe.
332+ func TestMultipleResets (t * testing.T ) {
333+ cp := & Calc {}
334+
335+ // Write some data to start goroutine
336+ _ , err := cp .Write ([]byte {0x42 })
337+ if err != nil {
338+ t .Fatalf ("Write failed: %v" , err )
339+ }
340+
341+ // Multiple resets are safe
342+ cp .Reset ()
343+ cp .Reset ()
344+ cp .Reset ()
345+ }
346+
347+ // TestReuseAfterDigestError verifies that a Calc can be reused after
348+ // Digest() returns an error, provided Reset() is called first.
349+ func TestReuseAfterDigestError (t * testing.T ) {
350+ cp := & Calc {}
351+
352+ // Write 64 bytes (one less than MinPiecePayload)
353+ data := make ([]byte , 64 )
354+ for i := range data {
355+ data [i ] = byte (i )
356+ }
357+ _ , err := cp .Write (data )
358+ if err != nil {
359+ t .Fatalf ("Write failed: %v" , err )
360+ }
361+
362+ // Digest fails due to insufficient data
363+ _ , _ , err = cp .Digest ()
364+ if err == nil {
365+ t .Fatal ("Expected error from Digest() with 64 bytes" )
366+ }
367+
368+ // Reset to clean up
369+ cp .Reset ()
370+
371+ // Calc is reusable after reset
372+ data2 := make ([]byte , 127 )
373+ for i := range data2 {
374+ data2 [i ] = byte (i )
375+ }
376+ _ , err = cp .Write (data2 )
377+ if err != nil {
378+ t .Fatalf ("Write after reset failed: %v" , err )
379+ }
380+
381+ commP , size , err := cp .Digest ()
382+ if err != nil {
383+ t .Fatalf ("Digest after reset failed: %v" , err )
384+ }
385+ if len (commP ) != 32 {
386+ t .Errorf ("Expected 32-byte commP, got %d" , len (commP ))
387+ }
388+ if size != 128 { // 127 bytes pads to 128 (FR32: 127 -> 128)
389+ t .Errorf ("Expected piece size 128, got %d" , size )
390+ }
391+ }
0 commit comments