]> git.giacomolli.uk - onechat/blobdiff - syncer_test.go
Describe child agent directories and limits
[onechat] / syncer_test.go
index 28a25973088fa5dd0ab3bbca43b212eee9750598..16f334530fc5def0b111ea702589926b3b209274 100644 (file)
@@ -15,12 +15,12 @@ import (
        "github.com/stretchr/testify/require"
 )
 
        "github.com/stretchr/testify/require"
 )
 
-// childDirVariable names the chat directory of the writer child process of
-// TestKilledWriterLosesNoLoggedMessage.
-const childDirVariable = "ONECHAT_TEST_CHILD_DIR"
+// syncerChildDirVariable names the chat directory of the writer child process
+// of TestKilledWriterLosesNoLoggedMessage.
+const syncerChildDirVariable = "ONECHAT_TEST_CHILD_DIR"
 
 func TestFlushWaitsForASyncThatStartsAfterTheWrite(t *testing.T) {
 
 func TestFlushWaitsForASyncThatStartsAfterTheWrite(t *testing.T) {
-       gate := newGatedSync()
+       gate := newSyncerGatedSync()
        s := newSyncer(gate.Sync)
        s.Written()
        gate.WaitStarted(t, 1)
        s := newSyncer(gate.Sync)
        s.Written()
        gate.WaitStarted(t, 1)
@@ -28,6 +28,7 @@ func TestFlushWaitsForASyncThatStartsAfterTheWrite(t *testing.T) {
        // This write comes during the first sync, so that sync does not cover
        // it.
        s.Written()
        // This write comes during the first sync, so that sync does not cover
        // it.
        s.Written()
+
        flushed := make(chan error)
        go func() {
                flushed <- s.Flush(t.Context())
        flushed := make(chan error)
        go func() {
                flushed <- s.Flush(t.Context())
@@ -35,6 +36,7 @@ func TestFlushWaitsForASyncThatStartsAfterTheWrite(t *testing.T) {
 
        gate.Release()
        gate.WaitStarted(t, 2)
 
        gate.Release()
        gate.WaitStarted(t, 2)
+
        select {
        case err := <-flushed:
                t.Fatalf("flush returned before the second sync: %v", err)
        select {
        case err := <-flushed:
                t.Fatalf("flush returned before the second sync: %v", err)
@@ -48,10 +50,11 @@ func TestFlushWaitsForASyncThatStartsAfterTheWrite(t *testing.T) {
 }
 
 func TestOneSyncCoversTheWritesDuringTheSyncBeforeIt(t *testing.T) {
 }
 
 func TestOneSyncCoversTheWritesDuringTheSyncBeforeIt(t *testing.T) {
-       gate := newGatedSync()
+       gate := newSyncerGatedSync()
        s := newSyncer(gate.Sync)
        s.Written()
        gate.WaitStarted(t, 1)
        s := newSyncer(gate.Sync)
        s.Written()
        gate.WaitStarted(t, 1)
+
        for range 10 {
                s.Written()
        }
        for range 10 {
                s.Written()
        }
@@ -65,7 +68,7 @@ func TestOneSyncCoversTheWritesDuringTheSyncBeforeIt(t *testing.T) {
 }
 
 func TestFlushWithoutWritesNeedsNoSync(t *testing.T) {
 }
 
 func TestFlushWithoutWritesNeedsNoSync(t *testing.T) {
-       gate := newGatedSync()
+       gate := newSyncerGatedSync()
        s := newSyncer(gate.Sync)
        require.NoError(t, s.Flush(t.Context()))
        require.NoError(t, s.Close())
        s := newSyncer(gate.Sync)
        require.NoError(t, s.Flush(t.Context()))
        require.NoError(t, s.Close())
@@ -86,7 +89,7 @@ func TestAFailedSyncFailsEachLaterFlush(t *testing.T) {
 }
 
 func TestMemoryFlushSyncsTheChat(t *testing.T) {
 }
 
 func TestMemoryFlushSyncsTheChat(t *testing.T) {
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        _, err = memory.Append("tool", "shell: ls")
        require.NoError(t, err)
 
        _, err = memory.Append("tool", "shell: ls")
@@ -98,16 +101,17 @@ func TestMemoryFlushSyncsTheChat(t *testing.T) {
 func TestKilledWriterLosesNoLoggedMessage(t *testing.T) {
        root := t.TempDir()
        for round := range 3 {
 func TestKilledWriterLosesNoLoggedMessage(t *testing.T) {
        root := t.TempDir()
        for round := range 3 {
-               acknowledged := runKilledWriter(t, root, 200+100*round)
+               acknowledged := syncerRunKilledWriter(t, root, 200+100*round)
 
 
-               memory, err := openMemory(t.Context(), root, time.Now, newFakeSummarizer())
+               memory, err := openMemory(t.Context(), root, time.Now, newFakeSummarizer(), discardLog())
                require.NoError(t, err)
                require.GreaterOrEqual(t, memory.log.Len(), acknowledged)
                require.NoError(t, err)
                require.GreaterOrEqual(t, memory.log.Len(), acknowledged)
+
                for id := range memory.log.Len() {
                        message, err := memory.log.Message(id)
                        require.NoError(t, err)
                for id := range memory.log.Len() {
                        message, err := memory.log.Message(id)
                        require.NoError(t, err)
-                       require.Equal(t, writerKind(id), message.Kind)
-                       require.Equal(t, writerText(id), message.Text)
+                       require.Equal(t, syncerWriterKind(id), message.Kind)
+                       require.Equal(t, syncerWriterText(id), message.Text)
                }
 
                require.NoError(t, memory.Close())
                }
 
                require.NoError(t, memory.Close())
@@ -115,19 +119,20 @@ func TestKilledWriterLosesNoLoggedMessage(t *testing.T) {
 }
 
 // TestWriterChild is the child process of TestKilledWriterLosesNoLoggedMessage.
 }
 
 // TestWriterChild is the child process of TestKilledWriterLosesNoLoggedMessage.
-// It appends messages to the chat in the directory of childDirVariable and
-// prints the ID of each message after its Append returns, until it is killed.
+// It appends messages to the chat in the directory of syncerChildDirVariable
+// and prints the ID of each message after its Append returns, until it is
+// killed.
 func TestWriterChild(t *testing.T) {
 func TestWriterChild(t *testing.T) {
-       root := os.Getenv(childDirVariable)
+       root := os.Getenv(syncerChildDirVariable)
        if root == "" {
                t.Skip("runs only as a child process")
        }
 
        if root == "" {
                t.Skip("runs only as a child process")
        }
 
-       memory, err := openMemory(t.Context(), root, time.Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), root, time.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        for id := memory.log.Len(); ; id++ {
        require.NoError(t, err)
 
        for id := memory.log.Len(); ; id++ {
-               _, err = memory.Append(writerKind(id), writerText(id))
+               _, err = memory.Append(syncerWriterKind(id), syncerWriterText(id))
                require.NoError(t, err)
 
                _, err = fmt.Println(id)
                require.NoError(t, err)
 
                _, err = fmt.Println(id)
@@ -135,34 +140,29 @@ func TestWriterChild(t *testing.T) {
        }
 }
 
        }
 }
 
-// gatedSync is a sync function that blocks until the test releases it.
-type gatedSync struct {
+// SyncerGatedSync is a sync function that blocks until the test releases it.
+type SyncerGatedSync struct {
        mutex   sync.Mutex
        count   int
        started chan struct{}
        release chan struct{}
 }
 
        mutex   sync.Mutex
        count   int
        started chan struct{}
        release chan struct{}
 }
 
-func newGatedSync() *gatedSync {
-       return &gatedSync{
-               started: make(chan struct{}, 100),
-               release: make(chan struct{}),
-       }
-}
-
-func (g *gatedSync) Sync() error {
+func (g *SyncerGatedSync) Sync() error {
        g.mutex.Lock()
        g.mutex.Lock()
+
        g.count++
        g.mutex.Unlock()
 
        g.started <- struct{}{}
        g.count++
        g.mutex.Unlock()
 
        g.started <- struct{}{}
+
        <-g.release
 
        return nil
 }
 
 // WaitStarted waits until sync number n has started.
        <-g.release
 
        return nil
 }
 
 // WaitStarted waits until sync number n has started.
-func (g *gatedSync) WaitStarted(t *testing.T, n int) {
+func (g *SyncerGatedSync) WaitStarted(t *testing.T, n int) {
        t.Helper()
 
        require.Eventually(t, func() bool {
        t.Helper()
 
        require.Eventually(t, func() bool {
@@ -171,26 +171,34 @@ func (g *gatedSync) WaitStarted(t *testing.T, n int) {
 }
 
 // Release lets the running sync end.
 }
 
 // Release lets the running sync end.
-func (g *gatedSync) Release() {
+func (g *SyncerGatedSync) Release() {
        <-g.started
        <-g.started
+
        g.release <- struct{}{}
 }
 
        g.release <- struct{}{}
 }
 
-func (g *gatedSync) Count() int {
+func (g *SyncerGatedSync) Count() int {
        g.mutex.Lock()
        defer g.mutex.Unlock()
 
        return g.count
 }
 
        g.mutex.Lock()
        defer g.mutex.Unlock()
 
        return g.count
 }
 
-// runKilledWriter starts the writer child on the chat in root, kills it with
-// SIGKILL after it acknowledged count messages, and returns the number of
+func newSyncerGatedSync() *SyncerGatedSync {
+       return &SyncerGatedSync{
+               started: make(chan struct{}, 100),
+               release: make(chan struct{}),
+       }
+}
+
+// syncerRunKilledWriter starts the writer child on the chat in root, kills it
+// with SIGKILL after it acknowledged count messages, and returns the number of
 // messages that it acknowledged.
 // messages that it acknowledged.
-func runKilledWriter(t *testing.T, root string, count int) int {
+func syncerRunKilledWriter(t *testing.T, root string, count int) int {
        t.Helper()
 
        command := exec.Command(os.Args[0], "-test.run=^TestWriterChild$")
        t.Helper()
 
        command := exec.Command(os.Args[0], "-test.run=^TestWriterChild$")
-       command.Env = append(os.Environ(), childDirVariable+"="+root)
+       command.Env = append(os.Environ(), syncerChildDirVariable+"="+root)
        stdout, err := command.StdoutPipe()
        require.NoError(t, err)
        require.NoError(t, command.Start())
        stdout, err := command.StdoutPipe()
        require.NoError(t, err)
        require.NoError(t, command.Start())
@@ -205,6 +213,7 @@ func runKilledWriter(t *testing.T, root string, count int) int {
        }
 
        require.NoError(t, command.Process.Kill())
        }
 
        require.NoError(t, command.Process.Kill())
+
        for scanner.Scan() {
                _, err = strconv.Atoi(strings.TrimSpace(scanner.Text()))
                if err == nil {
        for scanner.Scan() {
                _, err = strconv.Atoi(strings.TrimSpace(scanner.Text()))
                if err == nil {
@@ -218,14 +227,14 @@ func runKilledWriter(t *testing.T, root string, count int) int {
        return acknowledged
 }
 
        return acknowledged
 }
 
-// writerKind returns the kind of message id of the writer child.
-func writerKind(id int) string {
+// syncerWriterKind returns the kind of message id of the writer child.
+func syncerWriterKind(id int) string {
        return []string{"user", "tool", "echo", "reply"}[id%4]
 }
 
        return []string{"user", "tool", "echo", "reply"}[id%4]
 }
 
-// writerText returns the text of message id of the writer child. Some texts
-// are large, so a kill can come during the write of one line.
-func writerText(id int) string {
+// syncerWriterText returns the text of message id of the writer child. Some
+// texts are large, so a kill can come during the write of one line.
+func syncerWriterText(id int) string {
        size := 40 + id%7*30
        if id%5 == 4 {
                size = 64*1024 + id
        size := 40 + id%7*30
        if id%5 == 4 {
                size = 64*1024 + id