func (h *Hyperstart) SendIoString(seq uint64, data string) {
	h.SendIo(seq, []byte(data))
}
SendIoString sends a string a client connected the I/O channel.
Multiple I/O streams are multiplexed on that channel. seq specifies which
steam the data belongs to.
package mock
import (
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"net"
	"os"
	"path/filepath"
	"sync"
	"testing"
	hyper "github.com/containers/virtcontainers/pkg/hyperstart"
	"github.com/stretchr/testify/assert"
)
const (
	Version         = "version"
	StartPod        = "startpod"
	DestroyPod      = "destroypod"
	ExecCmd         = "execcmd"
	Ready           = "ready"
	Ack             = "ack"
	Error           = "error"
	WinSize         = "winsize"
	Ping            = "ping"
	FinishPod       = "finishpod"
	Next            = "next"
	WriteFile       = "writefile"
	ReadFile        = "readfile"
	NewContainer    = "newcontainer"
	KillContainer   = "killcontainer"
	RemoveContainer = "removecontainer"
	OnlineCPUMem    = "onlinecpumem"
	SetupInterface  = "setupinterface"
	SetupRoute      = "setuproute"
)
var codeList = map[int]string{
	hyper.VersionCode:         Version,
	hyper.StartPodCode:        StartPod,
	hyper.DestroyPodCode:      DestroyPod,
	hyper.ExecCmdCode:         ExecCmd,
	hyper.ReadyCode:           Ready,
	hyper.AckCode:             Ack,
	hyper.ErrorCode:           Error,
	hyper.WinsizeCode:         WinSize,
	hyper.PingCode:            Ping,
	hyper.NextCode:            Next,
	hyper.WriteFileCode:       WriteFile,
	hyper.ReadFileCode:        ReadFile,
	hyper.NewContainerCode:    NewContainer,
	hyper.KillContainerCode:   KillContainer,
	hyper.OnlineCPUMemCode:    OnlineCPUMem,
	hyper.SetupInterfaceCode:  SetupInterface,
	hyper.SetupRouteCode:      SetupRoute,
	hyper.RemoveContainerCode: RemoveContainer,
}
type Hyperstart struct {
	t                           *testing.T
	ctlSocketPath, ioSocketPath string
	ctlListener, ioListener     *net.UnixListener
	ctl, io                     net.Conn
	wgConnected sync.WaitGroup
	wg sync.WaitGroup
	lastMessages []hyper.DecodedMessage
}
func newMessageList() []hyper.DecodedMessage {
	return make([]hyper.DecodedMessage, 0, 10)
}
func NewHyperstart(t *testing.T) *Hyperstart {
	dir := os.TempDir()
	ctlSocketPath := filepath.Join(dir, "mock.hyper."+nextSuffix()+".0.sock")
	ioSocketPath := filepath.Join(dir, "mock.hyper."+nextSuffix()+".1.sock")
	return &Hyperstart{
		t:             t,
		ctlSocketPath: ctlSocketPath,
		ioSocketPath:  ioSocketPath,
		lastMessages:  newMessageList(),
	}
}
func (h *Hyperstart) GetSocketPaths() (string, string) {
	return h.ctlSocketPath, h.ioSocketPath
}
func (h *Hyperstart) GetLastMessages() []hyper.DecodedMessage {
	msgs := h.lastMessages
	h.lastMessages = newMessageList()
	return msgs
}
func (h *Hyperstart) log(s string) {
	h.logf("%s\n", s)
}
func (h *Hyperstart) logf(format string, args ...interface{}) {
	h.t.Logf("[hyperstart] "+format, args...)
}
func (h *Hyperstart) logData(data []byte) {
	h.t.Log(hex.Dump(data))
}
const ctlHeaderSize = 8
func (h *Hyperstart) writeCtl(data []byte) error {
	h.wgConnected.Wait()
	n, err := h.ctl.Write(data)
	if err != nil {
		return fmt.Errorf("Connection broken, cannot send data")
	}
	assert.Equal(h.t, n, len(data))
	return nil
}
func (h *Hyperstart) SendMessage(cmd int, data []byte) {
	length := ctlHeaderSize + len(data)
	header := make([]byte, ctlHeaderSize)
	binary.BigEndian.PutUint32(header[:], uint32(cmd))
	binary.BigEndian.PutUint32(header[4:], uint32(length))
	err := h.writeCtl(header)
	if err != nil {
		return
	}
	if len(data) == 0 {
		return
	}
	h.writeCtl(data)
}
func (h *Hyperstart) readCtl(data []byte) error {
	h.wgConnected.Wait()
	n, err := h.ctl.Read(data)
	if err != nil {
		return err
	}
	assert.Equal(h.t, n, len(data))
	return nil
}
func (h *Hyperstart) ackData(nBytes int) {
	data := make([]byte, 4)
	binary.BigEndian.PutUint32(data[:], uint32(nBytes))
	h.SendMessage(hyper.NextCode, data)
}
func (h *Hyperstart) readMessage() (int, []byte, error) {
	buf := make([]byte, ctlHeaderSize)
	if err := h.readCtl(buf); err != nil {
		return -1, buf, err
	}
	h.ackData(len(buf))
	cmd := int(binary.BigEndian.Uint32(buf[:4]))
	length := int(binary.BigEndian.Uint32(buf[4:8]))
	assert.True(h.t, length >= 8)
	length -= 8
	if length == 0 {
		return cmd, nil, nil
	}
	data := make([]byte, length)
	if err := h.readCtl(data); err != nil {
		return -1, buf, err
	}
	h.ackData(len(data))
	return cmd, data, nil
}
func cmdToString(cmd int) (string, error) {
	_, ok := codeList[cmd]
	if ok == false {
		return "", fmt.Errorf("unknown command '%d'", cmd)
	}
	return codeList[cmd], nil
}
func (h *Hyperstart) handleCtl() {
	for {
		cmd, data, err := h.readMessage()
		if err != nil {
			break
		}
		cmdName, err := cmdToString(cmd)
		assert.Nil(h.t, err)
		h.logf("ctl: --> command %s, payload_len=%d\n", cmdName, len(data))
		if len(data) != 0 {
			h.logData(data)
		}
		h.lastMessages = append(h.lastMessages, hyper.DecodedMessage{
			Code:    uint32(cmd),
			Message: data,
		})
		h.logf("ctl: <-- command %s executed successfully\n", cmdName)
		h.SendMessage(hyper.AckCode, nil)
	}
	h.wg.Done()
}
const ioHeaderSize = 12
func (h *Hyperstart) writeIo(data []byte) {
	h.wgConnected.Wait()
	n, err := h.io.Write(data)
	assert.Nil(h.t, err)
	assert.Equal(h.t, n, len(data))
}
func (h *Hyperstart) SendIo(seq uint64, data []byte) {
	length := ioHeaderSize + len(data)
	header := make([]byte, ioHeaderSize)
	h.logf("io: <-- writing %d bytes for seq %d\n", len(data), seq)
	binary.BigEndian.PutUint64(header[:], uint64(seq))
	binary.BigEndian.PutUint32(header[8:], uint32(length))
	h.writeIo(header)
	if len(data) == 0 {
		return
	}
	h.writeIo(data)
}