mirror of
https://github.com/metabarcoding/obitools4.git
synced 2026-11-09 17:24:41 +00:00
118 lines
2.9 KiB
Go
118 lines
2.9 KiB
Go
package obikmer
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"math/rand"
|
||
|
|
"reflect"
|
||
|
|
"sync"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
// The occurrence budget only bounds the memory: any budget, down to one
|
||
|
|
// occurrence (every partition then goes alone), gives the same counts.
|
||
|
|
func TestSeqKmerCounterBudgetDoesNotChangeTheCounts(t *testing.T) {
|
||
|
|
rng := rand.New(rand.NewSource(7))
|
||
|
|
seqs := randomSeqs(rng, 40, 20, 500, true)
|
||
|
|
build := func(budget int) []SeqKmerCount {
|
||
|
|
c, err := NewSeqKmerCounter(t.TempDir(), 9, 4, 11)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
defer c.Close()
|
||
|
|
if budget > 0 {
|
||
|
|
c.SetOccurrenceBudget(budget)
|
||
|
|
}
|
||
|
|
for i, s := range seqs {
|
||
|
|
c.AddSequence(uint32(i), s, i%2 == 0)
|
||
|
|
}
|
||
|
|
total := 0
|
||
|
|
for _, pw := range c.parts {
|
||
|
|
total += pw.kmers
|
||
|
|
}
|
||
|
|
if total == 0 {
|
||
|
|
t.Fatal("no occurrence counted")
|
||
|
|
}
|
||
|
|
got, err := c.CountAtLeast(2)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
return got
|
||
|
|
}
|
||
|
|
want := build(0)
|
||
|
|
if len(want) == 0 {
|
||
|
|
t.Fatal("the test needs k-mers shared by several sequences")
|
||
|
|
}
|
||
|
|
for _, budget := range []int{1, 100, 5000} {
|
||
|
|
if got := build(budget); !reflect.DeepEqual(got, want) {
|
||
|
|
t.Errorf("budget %d changes the counts", budget)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// The occurrences announced for a partition are those that its file holds.
|
||
|
|
func TestSeqKmerCounterPartitionOccurrenceCount(t *testing.T) {
|
||
|
|
rng := rand.New(rand.NewSource(3))
|
||
|
|
seqs := randomSeqs(rng, 25, 10, 300, true)
|
||
|
|
c, err := NewSeqKmerCounter(t.TempDir(), 8, 3, 5)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
defer c.Close()
|
||
|
|
for i, s := range seqs {
|
||
|
|
c.AddSequence(uint32(i), s, i%3 == 0)
|
||
|
|
}
|
||
|
|
if err := c.seal(); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
for p, pw := range c.parts {
|
||
|
|
occ, err := c.loadPartition(p, nil)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if len(occ) != pw.kmers {
|
||
|
|
t.Errorf("partition %d: %d occurrences loaded, %d counted when written", p, len(occ), pw.kmers)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestOccurrenceBudget(t *testing.T) {
|
||
|
|
b := newOccurrenceBudget(10)
|
||
|
|
if got := b.acquire(4); got != 4 {
|
||
|
|
t.Fatalf("acquire(4) = %d", got)
|
||
|
|
}
|
||
|
|
// 7 does not fit in the 6 left: it must wait for the release
|
||
|
|
done := make(chan int)
|
||
|
|
go func() { done <- b.acquire(7) }()
|
||
|
|
select {
|
||
|
|
case <-done:
|
||
|
|
t.Fatal("acquire(7) must wait")
|
||
|
|
case <-time.After(50 * time.Millisecond):
|
||
|
|
}
|
||
|
|
b.release(4)
|
||
|
|
if got := <-done; got != 7 {
|
||
|
|
t.Fatalf("acquire(7) = %d", got)
|
||
|
|
}
|
||
|
|
b.release(7)
|
||
|
|
|
||
|
|
// a request above the whole budget takes all of it, once everything is free
|
||
|
|
if got := b.acquire(1000); got != 10 {
|
||
|
|
t.Fatalf("an oversized request must take the whole budget, got %d", got)
|
||
|
|
}
|
||
|
|
var wg sync.WaitGroup
|
||
|
|
wg.Add(1)
|
||
|
|
go func() { defer wg.Done(); b.release(b.acquire(3)) }()
|
||
|
|
time.Sleep(20 * time.Millisecond)
|
||
|
|
b.release(10)
|
||
|
|
wg.Wait()
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestDefaultPartitions(t *testing.T) {
|
||
|
|
for _, tc := range []struct{ kmers, want int }{
|
||
|
|
{0, 256}, {5_000_000, 256}, {256 << 20, 256}, {257 << 20, 512}, {526_000_000, 512},
|
||
|
|
{600 << 20, 1024}, {1 << 40, 1024},
|
||
|
|
} {
|
||
|
|
if got := DefaultPartitions(tc.kmers); got != tc.want {
|
||
|
|
t.Errorf("DefaultPartitions(%d) = %d, want %d", tc.kmers, got, tc.want)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|