mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-23 11:27:42 +08:00
212 lines
5.0 KiB
Go
212 lines
5.0 KiB
Go
package harness
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
logging "github.com/ipfs/go-log/v2"
|
|
. "github.com/ipfs/kubo/test/cli/testutils"
|
|
"github.com/libp2p/go-libp2p/core/peer"
|
|
"github.com/multiformats/go-multiaddr"
|
|
)
|
|
|
|
// Harness tracks state for a test, such as temp dirs and IFPS nodes, and cleans them up after the test.
|
|
type Harness struct {
|
|
Dir string
|
|
IPFSBin string
|
|
Runner *Runner
|
|
NodesRoot string
|
|
Nodes Nodes
|
|
}
|
|
|
|
// TODO: use zaptest.NewLogger(t) instead
|
|
func EnableDebugLogging() {
|
|
err := logging.SetLogLevel("testharness", "DEBUG")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// NewT constructs a harness that cleans up after the given test is done.
|
|
func NewT(t *testing.T, options ...func(h *Harness)) *Harness {
|
|
h := New(options...)
|
|
t.Cleanup(h.Cleanup)
|
|
return h
|
|
}
|
|
|
|
func New(options ...func(h *Harness)) *Harness {
|
|
h := &Harness{Runner: &Runner{Env: osEnviron()}}
|
|
|
|
// walk up to find the root dir, from which we can locate the binary
|
|
wd, err := os.Getwd()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
goMod := FindUp("go.mod", wd)
|
|
if goMod == "" {
|
|
panic("unable to find root dir")
|
|
}
|
|
rootDir := filepath.Dir(goMod)
|
|
h.IPFSBin = filepath.Join(rootDir, "cmd", "ipfs", "ipfs")
|
|
|
|
// setup working dir
|
|
tmpDir, err := os.MkdirTemp("", "")
|
|
if err != nil {
|
|
log.Panicf("error creating temp dir: %s", err)
|
|
}
|
|
h.Dir = tmpDir
|
|
h.Runner.Dir = h.Dir
|
|
|
|
h.NodesRoot = filepath.Join(h.Dir, ".nodes")
|
|
|
|
// apply any customizations
|
|
// this should happen after all initialization
|
|
for _, o := range options {
|
|
o(h)
|
|
}
|
|
|
|
return h
|
|
}
|
|
|
|
func osEnviron() map[string]string {
|
|
m := map[string]string{}
|
|
for _, entry := range os.Environ() {
|
|
split := strings.Split(entry, "=")
|
|
m[split[0]] = split[1]
|
|
}
|
|
return m
|
|
}
|
|
|
|
func (h *Harness) NewNode() *Node {
|
|
nodeID := len(h.Nodes)
|
|
node := BuildNode(h.IPFSBin, h.NodesRoot, nodeID)
|
|
h.Nodes = append(h.Nodes, node)
|
|
return node
|
|
}
|
|
|
|
func (h *Harness) NewNodes(count int) Nodes {
|
|
var newNodes []*Node
|
|
for i := 0; i < count; i++ {
|
|
newNodes = append(newNodes, h.NewNode())
|
|
}
|
|
return newNodes
|
|
}
|
|
|
|
// WriteToTemp writes the given contents to a guaranteed-unique temp file, returning its path.
|
|
func (h *Harness) WriteToTemp(contents string) string {
|
|
f := h.TempFile()
|
|
_, err := f.WriteString(contents)
|
|
if err != nil {
|
|
log.Panicf("writing to temp file: %s", err.Error())
|
|
}
|
|
err = f.Close()
|
|
if err != nil {
|
|
log.Panicf("closing temp file: %s", err.Error())
|
|
}
|
|
return f.Name()
|
|
}
|
|
|
|
// TempFile creates a new unique temp file.
|
|
func (h *Harness) TempFile() *os.File {
|
|
f, err := os.CreateTemp(h.Dir, "")
|
|
if err != nil {
|
|
log.Panicf("creating temp file: %s", err.Error())
|
|
}
|
|
return f
|
|
}
|
|
|
|
// WriteFile writes a file given a filename and its contents.
|
|
// The filename must be a relative path, or this panics.
|
|
func (h *Harness) WriteFile(filename, contents string) {
|
|
if filepath.IsAbs(filename) {
|
|
log.Panicf("%s must be a relative path", filename)
|
|
}
|
|
absPath := filepath.Join(h.Runner.Dir, filename)
|
|
err := os.MkdirAll(filepath.Dir(absPath), 0o777)
|
|
if err != nil {
|
|
log.Panicf("creating intermediate dirs for %q: %s", filename, err.Error())
|
|
}
|
|
err = os.WriteFile(absPath, []byte(contents), 0o644)
|
|
if err != nil {
|
|
log.Panicf("writing %q (%q): %s", filename, absPath, err.Error())
|
|
}
|
|
}
|
|
|
|
func WaitForFile(path string, timeout time.Duration) error {
|
|
start := time.Now()
|
|
timer := time.NewTimer(timeout)
|
|
ticker := time.NewTicker(1 * time.Millisecond)
|
|
defer timer.Stop()
|
|
defer ticker.Stop()
|
|
for {
|
|
select {
|
|
case <-timer.C:
|
|
return fmt.Errorf("timeout waiting for %s after %v", path, time.Since(start))
|
|
case <-ticker.C:
|
|
_, err := os.Stat(path)
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
continue
|
|
}
|
|
return fmt.Errorf("error waiting for %s: %w", path, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (h *Harness) Mkdirs(paths ...string) {
|
|
for _, path := range paths {
|
|
if filepath.IsAbs(path) {
|
|
log.Panicf("%s must be a relative path when making dirs", path)
|
|
}
|
|
absPath := filepath.Join(h.Runner.Dir, path)
|
|
err := os.MkdirAll(absPath, 0o777)
|
|
if err != nil {
|
|
log.Panicf("recursively making dirs under %s: %s", absPath, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (h *Harness) Sh(expr string) *RunResult {
|
|
return h.Runner.Run(RunRequest{
|
|
Path: "bash",
|
|
Args: []string{"-c", expr},
|
|
})
|
|
}
|
|
|
|
func (h *Harness) Cleanup() {
|
|
log.Debugf("cleaning up cluster")
|
|
h.Nodes.StopDaemons()
|
|
// TODO: don't do this if test fails, not sure how?
|
|
log.Debugf("removing harness dir")
|
|
err := os.RemoveAll(h.Dir)
|
|
if err != nil {
|
|
log.Panicf("removing temp dir %s: %s", h.Dir, err)
|
|
}
|
|
}
|
|
|
|
// ExtractPeerID extracts a peer ID from the given multiaddr, and fatals if it does not contain a peer ID.
|
|
func (h *Harness) ExtractPeerID(m multiaddr.Multiaddr) peer.ID {
|
|
var peerIDStr string
|
|
multiaddr.ForEach(m, func(c multiaddr.Component) bool {
|
|
if c.Protocol().Code == multiaddr.P_P2P {
|
|
peerIDStr = c.Value()
|
|
}
|
|
return true
|
|
})
|
|
if peerIDStr == "" {
|
|
panic(multiaddr.ErrProtocolNotFound)
|
|
}
|
|
peerID, err := peer.Decode(peerIDStr)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return peerID
|
|
}
|