blob: f8615533a63d049464a10da7492272a9a33f9c58 [file] [edit]
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build linux
package pipewatcher
import (
"context"
"fmt"
"io"
"os"
"path"
"testing"
"time"
)
func TestPipe(t *testing.T) {
// Putting a directory name between temp dir and the file name guarantees we
// test the directory creation.
pipePath := path.Join(t.TempDir(), "ssh", "oslogin_trustedca.pub")
watcherID := "test-pipe-watcher-id"
eventID := "test-pipe,read"
opts := Options{
PipePath: pipePath,
Mode: 0755,
ReadEventID: eventID,
}
watcher := New(watcherID, opts)
testData := "test data transmited through the pipe."
if watcher.ID() != watcherID {
t.Errorf("Wrong watcher id, expected %s, got %s", watcherID, watcher.ID())
}
if len(watcher.Events()) == 0 {
t.Errorf("Watcher should have at least one event.")
}
timer := time.NewTimer(1 * time.Second)
// This go routine simulates the reading end of the pipe, it will until the timer
// is triggered (giving enough time for the Watcher to setup the pipe), when the
// read operation happened the Watcher will unblock returning to the test and
// the test implementation will write to the writing end of the pipe.
go func() {
<-timer.C
readFile, err := os.OpenFile(pipePath, os.O_RDONLY, 0644)
if err != nil {
t.Errorf("Failed to open the read end of the pipe: %+v", err)
return
}
defer func() {
if err := readFile.Close(); err != nil {
t.Errorf("Failed to close pipe(read end) file: %+v", err)
}
}()
buff := make([]byte, 1024)
var output string
for {
n, err := readFile.Read(buff)
if err == io.EOF {
break
}
if err != nil {
t.Errorf("Failed to read pipe: %+v", err)
return
}
if n > 0 {
output = fmt.Sprintf("%s%s", output, buff[:n])
}
}
if output != testData {
t.Errorf("Wrong data read from the pipe, got %s, want %s", output, testData)
}
}()
_, evData, err := watcher.Run(context.Background(), opts.ReadEventID)
if err != nil {
t.Fatalf("Watcher.Run(ReadEvent) failed: %s", err)
}
pipeData := evData.(*PipeData)
defer func() {
if err := pipeData.Close(); err != nil {
t.Fatalf("Failed to close pipe(write end) file: %s", err)
}
// Double closing should not produce an error.
if err := pipeData.Close(); err != nil {
t.Fatalf("Failed to close pipe(write end) file: %s", err)
}
pipeData.Finished()
}()
pipeData.WriteString(testData)
}
func TestCancel(t *testing.T) {
pipePath := path.Join(t.TempDir(), "ssh", "oslogin_trustedca.pub")
watcherID := "test-cancel-pipe-watcher-id"
opts := Options{
PipePath: pipePath,
Mode: 0755,
ReadEventID: "test-cancel-pipe,read",
}
watcher := New(watcherID, opts)
if len(watcher.Events()) == 0 {
t.Errorf("len(watcher.Events()) = %d, want > 0", len(watcher.Events()))
}
sync := make(chan bool)
defer close(sync)
cancelTimer := time.NewTimer((1 * time.Second) / 2)
timeoutTimer := time.NewTimer(1 * time.Second)
ctx, ctxCancel := context.WithCancel(context.Background())
go func() {
select {
case <-cancelTimer.C:
ctxCancel()
return
case <-timeoutTimer.C:
t.Error("Watcher should have been canceled before timeout.")
case <-sync:
return
}
}()
watcher.Run(ctx, opts.ReadEventID)
}