diff --git a/cmix/attempts/histrogram.go b/cmix/attempts/histrogram.go
index 52bf207a48345818f0f1900d5c69b45e0259d7d8..37e7292592aeae1cdda482a82b0fdcf5530933d9 100644
--- a/cmix/attempts/histrogram.go
+++ b/cmix/attempts/histrogram.go
@@ -1,49 +1,66 @@
 package attempts
 
 import (
+	"fmt"
 	"sort"
+	"strconv"
+	"strings"
 	"sync"
 	"sync/atomic"
 )
 
-const maxHistogramSize = 100
-const minElements = 3
-const percentileNumerator = 66
-const percentileDenominator = 99
-const percentileDenominatorOffset = 49
+const (
+	maxHistogramSize            = 100
+	minElements                 = 3
+	percentileNumerator         = 66
+	percentileDenominator       = 99
+	percentileDenominatorOffset = 49
+	optimalAttemptsInitValue    = -1
+)
 
+// SendAttemptTracker tracks the number of attempts it took to send a cMix
+// message in order to predict how many attempt are needed.
 type SendAttemptTracker interface {
+	// SubmitProbeAttempt feeds the number of attempts it took to send a cMix
+	// message into the tracker and updates the optimal number of attempts.
 	SubmitProbeAttempt(numAttemptsUntilSuccessful int)
+
+	// GetOptimalNumAttempts returns the number of optimal sends. If there is
+	// insufficient data to calculate, then ready is false.
 	GetOptimalNumAttempts() (attempts int, ready bool)
 }
 
+// sendAttempts tracks the number of attempts to send a cMix message.
 type sendAttempts struct {
-	lock         sync.Mutex
-	numAttempts  []int
-	currentIndex int
-	isFull       bool
-
 	optimalAttempts *int32
+	isFull          bool
+	currentIndex    int
+	numAttempts     []int
+	lock            sync.Mutex
 }
 
+// NewSendAttempts initialises a new SendAttemptTracker.
 func NewSendAttempts() SendAttemptTracker {
-	optimalAttempts := int32(-1)
-
+	optimalAttempts := int32(optimalAttemptsInitValue)
 	sa := &sendAttempts{
-		numAttempts:     make([]int, maxHistogramSize),
-		currentIndex:    0,
-		isFull:          false,
 		optimalAttempts: &optimalAttempts,
+		isFull:          false,
+		currentIndex:    0,
+		numAttempts:     make([]int, maxHistogramSize),
 	}
+
 	return sa
 }
 
-func (sa *sendAttempts) SubmitProbeAttempt(a int) {
+// SubmitProbeAttempt feeds the number of attempts it took to send a cMix
+// message into the tracker and updates the optimal number of attempts.
+func (sa *sendAttempts) SubmitProbeAttempt(numAttemptsUntilSuccessful int) {
 	sa.lock.Lock()
 	defer sa.lock.Unlock()
 
-	sa.numAttempts[sa.currentIndex] = a
-	sa.currentIndex += 1
+	sa.numAttempts[sa.currentIndex] = numAttemptsUntilSuccessful
+	sa.currentIndex++
+
 	if sa.currentIndex == len(sa.numAttempts) {
 		sa.currentIndex = 0
 		sa.isFull = true
@@ -52,16 +69,19 @@ func (sa *sendAttempts) SubmitProbeAttempt(a int) {
 	sa.computeOptimalUnsafe()
 }
 
+// GetOptimalNumAttempts returns the number of optimal sends. If there is
+// insufficient data to calculate, then ready is false.
 func (sa *sendAttempts) GetOptimalNumAttempts() (attempts int, ready bool) {
 	optimalAttempts := atomic.LoadInt32(sa.optimalAttempts)
 
-	if optimalAttempts == -1 {
+	if optimalAttempts == optimalAttemptsInitValue {
 		return 0, false
 	}
 
 	return int(optimalAttempts), true
 }
 
+// computeOptimalUnsafe updates the optimal send attempts.
 func (sa *sendAttempts) computeOptimalUnsafe() {
 	toCopy := maxHistogramSize
 	if !sa.isFull {
@@ -69,16 +89,28 @@ func (sa *sendAttempts) computeOptimalUnsafe() {
 			return
 		}
 		toCopy = sa.currentIndex
-
 	}
 
-	histoCopy := make([]int, toCopy)
-	copy(histoCopy, sa.numAttempts[:toCopy])
-
-	sort.Slice(histoCopy, func(i, j int) bool {
-		return histoCopy[i] < histoCopy[j]
-	})
+	histogramCopy := make([]int, toCopy)
+	copy(histogramCopy, sa.numAttempts[:toCopy])
+	sort.Ints(histogramCopy)
 
-	optimal := histoCopy[((toCopy*percentileNumerator)+percentileDenominatorOffset)/percentileDenominator]
+	i := ((toCopy * percentileNumerator) + percentileDenominatorOffset) /
+		percentileDenominator
+	optimal := histogramCopy[i]
 	atomic.StoreInt32(sa.optimalAttempts, int32(optimal))
 }
+
+// String prints the values in the sendAttempts in a human-readable form for
+// debugging and logging purposes. This function adheres to the fmt.Stringer
+// interface.
+func (sa *sendAttempts) String() string {
+	fields := []string{
+		"optimalAttempts:" + strconv.Itoa(int(atomic.LoadInt32(sa.optimalAttempts))),
+		"isFull:" + strconv.FormatBool(sa.isFull),
+		"currentIndex:" + strconv.Itoa(sa.currentIndex),
+		"numAttempts:" + fmt.Sprintf("%d", sa.numAttempts),
+	}
+
+	return "{" + strings.Join(fields, " ") + "}"
+}
diff --git a/cmix/attempts/histrogram_test.go b/cmix/attempts/histrogram_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..6934b1f332e44cd17dd5cf2030932a8ec364fbd3
--- /dev/null
+++ b/cmix/attempts/histrogram_test.go
@@ -0,0 +1,89 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 xx foundation                                             //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file.                                                              //
+////////////////////////////////////////////////////////////////////////////////
+
+package attempts
+
+import (
+	"math/rand"
+	"reflect"
+	"testing"
+)
+
+// Tests that NewSendAttempts returns a new sendAttempts with the expected
+// fields.
+func TestNewSendAttempts(t *testing.T) {
+	optimalAttempts := int32(optimalAttemptsInitValue)
+	expected := &sendAttempts{
+		optimalAttempts: &optimalAttempts,
+		isFull:          false,
+		currentIndex:    0,
+		numAttempts:     make([]int, maxHistogramSize),
+	}
+
+	sa := NewSendAttempts()
+
+	if !reflect.DeepEqual(expected, sa) {
+		t.Errorf("New SendAttemptTracker does not match expected."+
+			"\nexpected: %+v\nreceivedL %+v", expected, sa)
+	}
+}
+
+// Tests that sendAttempts.SubmitProbeAttempt properly increments and stores the
+// attempts.
+func Test_sendAttempts_SubmitProbeAttempt(t *testing.T) {
+	sa := NewSendAttempts().(*sendAttempts)
+
+	for i := 0; i < maxHistogramSize+20; i++ {
+		sa.SubmitProbeAttempt(i)
+
+		if sa.currentIndex != (i+1)%maxHistogramSize {
+			t.Errorf("Incorrect currentIndex (%d).\nexpected: %d\nreceived: %d",
+				i, (i+1)%maxHistogramSize, sa.currentIndex)
+		} else if sa.numAttempts[i%maxHistogramSize] != i {
+			t.Errorf("Incorrect numAttempts at %d.\nexpected: %d\nreceived: %d",
+				i, i, sa.numAttempts[i%maxHistogramSize])
+		} else if i > maxHistogramSize && !sa.isFull {
+			t.Errorf("Should be marked full when numAttempts > %d.",
+				maxHistogramSize)
+		}
+	}
+}
+
+// Tests sendAttempts.GetOptimalNumAttempts returns numbers close to 70% of the
+// average of attempts feeding in.
+func Test_sendAttempts_GetOptimalNumAttempts(t *testing.T) {
+	prng := rand.New(rand.NewSource(42))
+	sa := NewSendAttempts().(*sendAttempts)
+
+	attempts, ready := sa.GetOptimalNumAttempts()
+	if ready {
+		t.Errorf("Marked ready when no attempts have been made.")
+	} else if attempts != 0 {
+		t.Errorf("Incorrect number of attempt.\nexpected: %d\nreceived: %d",
+			0, attempts)
+	}
+
+	const n = 100
+	factor := (n * 7) / 10
+	for i := 0; i < 500; i++ {
+		sa.SubmitProbeAttempt(prng.Intn(n))
+		attempts, ready = sa.GetOptimalNumAttempts()
+
+		if (sa.currentIndex < minElements && !sa.isFull) && ready {
+			t.Errorf("Ready when less than %d attempts made (%d).",
+				minElements, i)
+		} else if sa.currentIndex >= minElements {
+			if !ready {
+				t.Errorf("Not ready when more than %d attempts made (%d).",
+					minElements, i)
+			} else if attempts < factor-25 || attempts > factor+25 {
+				t.Errorf("Attempts is not close to average (%d)."+
+					"\naverage:  %d\nattempts: %d", i, factor, attempts)
+			}
+		}
+	}
+}