<commit_before>package renter

import (
	"bytes"
	"crypto/rand"
	"testing"
	"time"
)

func (h *testHost) addPiece(p uploadPiece) error {
	h.pieceMap[p.chunkIndex] = append(h.pieceMap[p.chunkIndex], pieceData{
		p.chunkIndex,
		p.pieceIndex,
		uint64(len(h.data)),
	})
	h.data = append(h.data, p.data...)
	// simulate I/O delay
	time.Sleep(h.delay)
	return nil
}

func (h *testHost) fileContract() fileContract { return fileContract{} }

// TestErasureUpload tests parallel uploading of erasure-coded data.
func TestErasureUpload(t *testing.T) {
	// generate data
	const dataSize = 777
	data := make([]byte, dataSize)
	rand.Read(data)

	// create Reed-Solomon encoder
	rsc, err := NewRSCode(2, 10)
	if err != nil {
		t.Fatal(err)
	}

	// create hosts
	hosts := make([]uploader, rsc.NumPieces())
	for i := range hosts {
		hosts[i] = &testHost{
			pieceMap: make(map[uint64][]pieceData),
			delay:    time.Duration(i) * time.Millisecond,
		}
	}
	// make one host really slow
	hosts[0].(*testHost).delay = 100 * time.Millisecond

	// upload data to hosts
	const pieceSize = 10
	f := newFile("foo", rsc, pieceSize, dataSize)
	err = f.upload(bytes.NewReader(data), hosts)
	if err != nil {
		t.Fatal(err)
	}

	// download data
	buf := new(bytes.Buffer)
	chunk := make([][]byte, rsc.NumPieces())
	for i := uint64(0); i < f.numChunks(); i++ {
		for _, h := range hosts {
			host := h.(*testHost)
			for _, p := range host.pieceMap[i] {
				chunk[p.Piece] = host.data[p.Offset : p.Offset+pieceSize]
			}
		}
		err = rsc.Recover(chunk, f.chunkSize(), buf)
		if err != nil {
			t.Fatal(err)
		}
	}
	buf.Truncate(dataSize)

	if !bytes.Equal(buf.Bytes(), data) {<commit_msg>introduce random failure in upload test<commit_after>package renter

import (
	"bytes"
	"crypto/rand"
	"testing"
	"time"

	"github.com/NebulousLabs/Sia/crypto"
)

func (h *testHost) addPiece(p uploadPiece) error {
	// simulate I/O delay
	time.Sleep(h.delay)

	// randomly fail
	if n, _ := crypto.RandIntn(h.failRate); n == 0 {
		return crypto.ErrNilInput
	}

	h.pieceMap[p.chunkIndex] = append(h.pieceMap[p.chunkIndex], pieceData{
		p.chunkIndex,
		p.pieceIndex,
		uint64(len(h.data)),
	})
	h.data = append(h.data, p.data...)
	return nil
}

func (h *testHost) fileContract() fileContract { return fileContract{} }

// TestErasureUpload tests parallel uploading of erasure-coded data.
func TestErasureUpload(t *testing.T) {
	// generate data
	const dataSize = 777
	data := make([]byte, dataSize)
	rand.Read(data)

	// create Reed-Solomon encoder
	rsc, err := NewRSCode(2, 10)
	if err != nil {
		t.Fatal(err)
	}

	// create hosts
	const pieceSize = 10
	hosts := make([]uploader, rsc.NumPieces())
	for i := range hosts {
		hosts[i] = &testHost{
			pieceMap:  make(map[uint64][]pieceData),
			pieceSize: pieceSize,
			delay:     time.Duration(i) * time.Millisecond,
			failRate:  5, // 20% failure rate
		}
	}
	// make one host really slow
	hosts[0].(*testHost).delay = 100 * time.Millisecond
	// make one host always fail
	hosts[1].(*testHost).failRate = 1

	// upload data to hosts
	f := newFile("foo", rsc, pieceSize, dataSize)
	err = f.upload(bytes.NewReader(data), hosts)
	if err != nil {
		t.Fatal(err)
	}

	// download data
	buf := new(bytes.Buffer)
	for i := uint64(0); i < f.numChunks(); i++ {
		chunk := make([][]byte, rsc.NumPieces())
		for _, h := range hosts {
			host := h.(*testHost)
			for _, p := range host.pieceMap[i] {
				chunk[p.Piece] = host.data[p.Offset : p.Offset+pieceSize]
			}
		}
		err = rsc.Recover(chunk, f.chunkSize(), buf)
		if err != nil {
			t.Fatal(err)
		}
	}
	buf.Truncate(dataSize)

	if !bytes.Equal(buf.Bytes(), data) {
{'fraction_non_alphanumeric': 0.16316371681415928, 'fraction_numerical': 0.012168141592920354, 'mean_word_length': 3.5043586550435863, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3925089', 'n_tokens_mistral': 1509, 'n_tokens_neox': 1380, 'n_words': 321}