github.com/ks888/tgo@v0.0.0-20190130135156-80bf89407292/debugapi/client_darwin_test.go (about)

     1  package debugapi
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"net"
     7  	"os"
     8  	"os/exec"
     9  	"path"
    10  	"strconv"
    11  	"strings"
    12  	"syscall"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/ks888/tgo/testutils"
    17  	"golang.org/x/sys/unix"
    18  )
    19  
    20  func TestCheckInterface(t *testing.T) {
    21  	var _ client = NewClient()
    22  }
    23  
    24  func TestLaunchProcess(t *testing.T) {
    25  	client := NewClient()
    26  	err := client.LaunchProcess(testutils.ProgramInfloop)
    27  	if err != nil {
    28  		t.Fatalf("failed to launch process: %v", err)
    29  	}
    30  	defer client.DetachProcess()
    31  }
    32  
    33  func TestLaunchProcess_ProgramNotExist(t *testing.T) {
    34  	client := NewClient()
    35  	err := client.LaunchProcess("notexist")
    36  	if err == nil {
    37  		t.Fatalf("error not returned")
    38  	}
    39  }
    40  
    41  func TestAttachProcess(t *testing.T) {
    42  	cmd := exec.Command(testutils.ProgramInfloop)
    43  	_ = cmd.Start()
    44  
    45  	client := NewClient()
    46  	err := client.AttachProcess(cmd.Process.Pid)
    47  	if err != nil {
    48  		t.Fatalf("failed to launch process: %v", err)
    49  	}
    50  	client.DetachProcess() // must detach before kill. Otherwise, the program becomes zombie.
    51  	cmd.Process.Kill()
    52  	cmd.Process.Wait()
    53  }
    54  
    55  func TestAttachProcess_WrongPID(t *testing.T) {
    56  	client := NewClient()
    57  	cmd := exec.Command(testutils.ProgramHelloworld)
    58  	_ = cmd.Run()
    59  
    60  	// the program already exits, so the pid is wrong
    61  	err := client.AttachProcess(cmd.Process.Pid)
    62  	if err == nil {
    63  		t.Fatalf("error should be returned")
    64  	}
    65  }
    66  
    67  func TestDetachProcess_KillProc(t *testing.T) {
    68  	client := NewClient()
    69  	err := client.LaunchProcess(testutils.ProgramInfloop)
    70  	if err != nil {
    71  		t.Fatalf("failed to launch process: %v", err)
    72  	}
    73  	defer client.DetachProcess()
    74  
    75  	debugeeProcID, _ := findProcessID(path.Base(testutils.ProgramInfloop), client.pid)
    76  
    77  	if err := client.DetachProcess(); err != nil {
    78  		t.Fatalf("failed to detach from the process: %v", err)
    79  	}
    80  
    81  	// it often takes some times to finish the debug server and debugee.
    82  	for i := 0; i < 10; i++ {
    83  		if !existsPid(debugeeProcID) {
    84  			break
    85  		}
    86  		time.Sleep(100 * time.Millisecond)
    87  	}
    88  	if existsPid(debugeeProcID) {
    89  		t.Errorf("the debugee process is still alive")
    90  	}
    91  }
    92  
    93  func TestReadRegisters(t *testing.T) {
    94  	client := NewClient()
    95  	err := client.LaunchProcess(testutils.ProgramInfloop)
    96  	if err != nil {
    97  		t.Fatalf("failed to launch process: %v", err)
    98  	}
    99  	defer client.DetachProcess()
   100  
   101  	if err := client.WriteMemory(testutils.InfloopAddrMain, []byte{0xcc}); err != nil {
   102  		t.Fatalf("failed to write memory: %v", err)
   103  	}
   104  	event, err := client.ContinueAndWait()
   105  	if err != nil {
   106  		t.Fatalf("failed to continue and wait: %v", err)
   107  	}
   108  	threadIDs := event.Data.([]int)
   109  
   110  	regs, err := client.ReadRegisters(threadIDs[0])
   111  	if err != nil {
   112  		t.Fatalf("failed to read registers: %v", err)
   113  	}
   114  	if regs.Rip != uint64(testutils.InfloopAddrMain+1) {
   115  		t.Fatalf("wrong rip: %x", regs.Rip)
   116  	}
   117  	if regs.Rsp == 0 {
   118  		t.Fatalf("empty rsp: %x", regs.Rsp)
   119  	}
   120  }
   121  
   122  func TestWriteRegisters(t *testing.T) {
   123  	client := NewClient()
   124  	err := client.LaunchProcess(testutils.ProgramInfloop)
   125  	if err != nil {
   126  		t.Fatalf("failed to launch process: %v", err)
   127  	}
   128  	defer client.DetachProcess()
   129  
   130  	threadIDs, err := client.ThreadIDs()
   131  	if err != nil {
   132  		t.Fatalf("failed to get thread ids: %v", err)
   133  	}
   134  
   135  	regs := Registers{Rip: 0x1, Rsp: 0x2, Rcx: 0x3}
   136  	if err := client.WriteRegisters(threadIDs[0], regs); err != nil {
   137  		t.Fatalf("failed to write registers: %v", err)
   138  	}
   139  
   140  	actualRegs, _ := client.ReadRegisters(threadIDs[0])
   141  	if actualRegs.Rip != 0x1 {
   142  		t.Errorf("wrong rip: %x", actualRegs.Rip)
   143  	}
   144  	if actualRegs.Rsp != 0x2 {
   145  		t.Errorf("wrong rsp: %x", actualRegs.Rsp)
   146  	}
   147  	if actualRegs.Rcx != 0x3 {
   148  		t.Errorf("wrong rcx: %x", actualRegs.Rcx)
   149  	}
   150  }
   151  
   152  func TestAllocateMemory(t *testing.T) {
   153  	client := NewClient()
   154  	err := client.LaunchProcess(testutils.ProgramInfloop)
   155  	if err != nil {
   156  		t.Fatalf("failed to launch process: %v", err)
   157  	}
   158  	defer client.DetachProcess()
   159  
   160  	addr, err := client.allocateMemory(1)
   161  	if err != nil {
   162  		t.Fatalf("failed to allocate memory: %v", err)
   163  	}
   164  
   165  	if addr == 0 {
   166  		t.Errorf("empty addr: %x", addr)
   167  	}
   168  }
   169  
   170  func TestDeallocateMemory(t *testing.T) {
   171  	client := NewClient()
   172  	err := client.LaunchProcess(testutils.ProgramInfloop)
   173  	if err != nil {
   174  		t.Fatalf("failed to launch process: %v", err)
   175  	}
   176  	defer client.DetachProcess()
   177  
   178  	addr, _ := client.allocateMemory(1)
   179  	err = client.deallocateMemory(addr)
   180  	if err != nil {
   181  		t.Fatalf("failed to deallocate memory: %v", err)
   182  	}
   183  }
   184  
   185  func TestReadMemory(t *testing.T) {
   186  	client := NewClient()
   187  	err := client.LaunchProcess(testutils.ProgramInfloop)
   188  	if err != nil {
   189  		t.Fatalf("failed to launch process: %v", err)
   190  	}
   191  	defer client.DetachProcess()
   192  
   193  	out := make([]byte, 2)
   194  	err = client.ReadMemory(testutils.InfloopAddrMain, out)
   195  	if err != nil {
   196  		t.Fatalf("failed to read memory: %v", err)
   197  	}
   198  
   199  	if out[0] != 0x65 || out[1] != 0x48 {
   200  		t.Errorf("wrong memory: %v", out)
   201  	}
   202  }
   203  
   204  func TestReadMemory_LargeSize(t *testing.T) {
   205  	client := NewClient()
   206  	err := client.LaunchProcess(testutils.ProgramInfloop)
   207  	if err != nil {
   208  		t.Fatalf("failed to launch process: %v", err)
   209  	}
   210  	defer client.DetachProcess()
   211  
   212  	out := make([]byte, 2048)
   213  	err = client.ReadMemory(testutils.InfloopAddrMain, out)
   214  	if err != nil {
   215  		t.Fatalf("failed to read memory: %v", err)
   216  	}
   217  
   218  	if out[0] != 0x65 || out[1] != 0x48 {
   219  		t.Errorf("wrong memory: %v", out)
   220  	}
   221  }
   222  
   223  func TestWriteMemory(t *testing.T) {
   224  	client := NewClient()
   225  	err := client.LaunchProcess(testutils.ProgramInfloop)
   226  	if err != nil {
   227  		t.Fatalf("failed to launch process: %v", err)
   228  	}
   229  	defer client.DetachProcess()
   230  
   231  	data := []byte{0x1, 0x2, 0x3, 0x4}
   232  	err = client.WriteMemory(testutils.InfloopAddrMain, data)
   233  	if err != nil {
   234  		t.Fatalf("failed to write memory: %v", err)
   235  	}
   236  
   237  	actual := make([]byte, 4)
   238  	_ = client.ReadMemory(testutils.InfloopAddrMain, actual)
   239  	if actual[0] != 0x1 || actual[1] != 0x2 || actual[2] != 0x3 || actual[3] != 0x4 {
   240  		t.Errorf("wrong memory: %v", actual)
   241  	}
   242  
   243  }
   244  
   245  func TestReadTLS(t *testing.T) {
   246  	client := NewClient()
   247  	err := client.LaunchProcess(testutils.ProgramInfloop)
   248  	if err != nil {
   249  		t.Fatalf("failed to launch process: %v", err)
   250  	}
   251  	defer client.DetachProcess()
   252  
   253  	_ = client.WriteMemory(testutils.InfloopAddrMain, []byte{0xcc})
   254  	event, _ := client.ContinueAndWait()
   255  	threadIDs := event.Data.([]int)
   256  
   257  	_, err = client.ReadTLS(threadIDs[0], 0xf)
   258  	if err != nil {
   259  		t.Fatalf("failed to read tls: %v", err)
   260  	}
   261  }
   262  
   263  func TestContinueAndWait_Trapped(t *testing.T) {
   264  	client := NewClient()
   265  	err := client.LaunchProcess(testutils.ProgramInfloop)
   266  	if err != nil {
   267  		t.Fatalf("failed to launch process: %v", err)
   268  	}
   269  	defer client.DetachProcess()
   270  
   271  	out := []byte{0xcc}
   272  	err = client.WriteMemory(testutils.InfloopAddrMain, out)
   273  	if err != nil {
   274  		t.Fatalf("failed to write memory: %v", err)
   275  	}
   276  
   277  	event, err := client.ContinueAndWait()
   278  	threadIDs := event.Data.([]int)
   279  	if err != nil {
   280  		t.Fatalf("failed to continue and wait: %v", err)
   281  	}
   282  	if len(threadIDs) == 0 {
   283  		t.Errorf("empty threadIDs")
   284  	}
   285  	if event.Type != EventTypeTrapped {
   286  		t.Errorf("wrong event type: %v", event.Type)
   287  	}
   288  }
   289  
   290  func TestContinueAndWait_Exited(t *testing.T) {
   291  	client := NewClient()
   292  	err := client.LaunchProcess(testutils.ProgramHelloworld)
   293  	if err != nil {
   294  		t.Fatalf("failed to launch process: %v", err)
   295  	}
   296  	defer client.DetachProcess()
   297  
   298  	for {
   299  		event, err := client.ContinueAndWait()
   300  		if err != nil {
   301  			t.Fatalf("failed to continue and wait: %v", err)
   302  		}
   303  		if event == (Event{Type: EventTypeExited, Data: 0}) {
   304  			break
   305  		}
   306  	}
   307  }
   308  
   309  func TestContinueAndWait_ConsoleWrite(t *testing.T) {
   310  	client := NewClient()
   311  	buff := &bytes.Buffer{}
   312  	client.outputWriter = buff
   313  	err := client.LaunchProcess(testutils.ProgramHelloworld)
   314  	if err != nil {
   315  		t.Fatalf("failed to launch process: %v", err)
   316  	}
   317  	defer client.DetachProcess()
   318  
   319  	for {
   320  		_, err := client.ContinueAndWait()
   321  		if err != nil {
   322  			t.Fatalf("failed to continue and wait: %v", err)
   323  		}
   324  		if strings.Contains(buff.String(), "Hello world") {
   325  			break
   326  		}
   327  	}
   328  }
   329  
   330  func TestContinueAndWait_Signaled(t *testing.T) {
   331  	client := NewClient()
   332  	err := client.LaunchProcess(testutils.ProgramInfloop)
   333  	if err != nil {
   334  		t.Fatalf("failed to launch process: %v", err)
   335  	}
   336  	defer client.DetachProcess()
   337  
   338  	pid, _ := findProcessID(path.Base(testutils.ProgramInfloop), client.pid)
   339  	_ = sendSignal(pid, unix.SIGKILL) // SIGTERM is not passed to the debugee
   340  
   341  	event, err := client.ContinueAndWait()
   342  	if err != nil {
   343  		t.Fatalf("failed to continue and wait: %v", err)
   344  	}
   345  	if event != (Event{Type: EventTypeTerminated, Data: 0}) {
   346  		t.Fatalf("wrong event: %v", event)
   347  	}
   348  }
   349  
   350  // No test for CoreDump as the debugserver does not pass the signals like SIGQUIT to the debugee.
   351  
   352  func TestStepAndWait(t *testing.T) {
   353  	client := NewClient()
   354  	err := client.LaunchProcess(testutils.ProgramInfloop)
   355  	if err != nil {
   356  		t.Fatalf("failed to launch process: %v", err)
   357  	}
   358  	defer client.DetachProcess()
   359  
   360  	threadIDs, err := client.ThreadIDs()
   361  	if err != nil {
   362  		t.Fatalf("failed to get thread ids: %v", err)
   363  	}
   364  
   365  	event, err := client.StepAndWait(threadIDs[0])
   366  	if err != nil {
   367  		t.Fatalf("failed to step and wait: %v", err)
   368  	}
   369  	if event.Type != EventTypeTrapped {
   370  		t.Fatalf("wrong event type: %v", event.Type)
   371  	}
   372  }
   373  
   374  func TestStepAndWait_StopAtBreakpoint(t *testing.T) {
   375  	client := NewClient()
   376  	err := client.LaunchProcess(testutils.ProgramInfloop)
   377  	if err != nil {
   378  		t.Fatalf("failed to launch process: %v", err)
   379  	}
   380  	defer client.DetachProcess()
   381  
   382  	orgInsts := make([]byte, 1)
   383  	_ = client.ReadMemory(testutils.InfloopAddrMain, orgInsts)
   384  	_ = client.WriteMemory(testutils.InfloopAddrMain, []byte{0xcc})
   385  	event, _ := client.ContinueAndWait()
   386  	threadIDs := event.Data.([]int)
   387  
   388  	regs, _ := client.ReadRegisters(threadIDs[0])
   389  	regs.Rip--
   390  	_ = client.WriteRegisters(threadIDs[0], regs)
   391  	_ = client.WriteMemory(testutils.InfloopAddrMain, orgInsts)
   392  
   393  	_, err = client.StepAndWait(threadIDs[0])
   394  	if err != nil {
   395  		t.Fatalf("failed to step and wait: %v", err)
   396  	}
   397  
   398  	regs, _ = client.ReadRegisters(threadIDs[0])
   399  	if regs.Rip != uint64(testutils.InfloopAddrMain)+9 {
   400  		t.Errorf("wrong pc: %x", regs.Rip)
   401  	}
   402  }
   403  
   404  func TestStepAndWait_UnspecifiedThread(t *testing.T) {
   405  	client := NewClient()
   406  	err := client.LaunchProcess(testutils.ProgramInfloop)
   407  	if err != nil {
   408  		t.Fatalf("failed to launch process: %v", err)
   409  	}
   410  	defer client.DetachProcess()
   411  
   412  	orgInsts := make([]byte, 1)
   413  	_ = client.ReadMemory(testutils.InfloopAddrMain, orgInsts)
   414  	_ = client.WriteMemory(testutils.InfloopAddrMain, []byte{0xcc})
   415  	event, _ := client.ContinueAndWait()
   416  	threadIDs := event.Data.([]int)
   417  
   418  	regs, _ := client.ReadRegisters(threadIDs[0])
   419  	regs.Rip--
   420  	_ = client.WriteRegisters(threadIDs[0], regs)
   421  	_ = client.WriteMemory(testutils.InfloopAddrMain, orgInsts)
   422  
   423  	_, err = client.StepAndWait(0)
   424  	if _, ok := err.(UnspecifiedThreadError); !ok {
   425  		t.Fatalf("not UnspecifiedThreadError: %v", err)
   426  	}
   427  	fmt.Println(err)
   428  }
   429  
   430  func findProcessID(progName string, parentPID int) (int, error) {
   431  	out, err := exec.Command("pgrep", "-P", strconv.Itoa(parentPID), progName).Output()
   432  	if err != nil {
   433  		return 0, err
   434  	}
   435  
   436  	return strconv.Atoi(string(out[0 : len(out)-1])) // remove newline
   437  }
   438  
   439  func existsPid(pid int) bool {
   440  	process, err := os.FindProcess(pid)
   441  	if err != nil {
   442  		return false
   443  	}
   444  
   445  	// Signal 0 can be used to check the validity of pid.
   446  	return process.Signal(syscall.Signal(0)) == nil
   447  }
   448  
   449  func sendSignal(pid int, signal syscall.Signal) error {
   450  	proc, err := os.FindProcess(pid)
   451  	if err != nil {
   452  		return err
   453  	}
   454  
   455  	return proc.Signal(signal)
   456  }
   457  
   458  func TestSetNoAckMode(t *testing.T) {
   459  	connForReceive, connForSend := net.Pipe()
   460  
   461  	sendDone := make(chan bool)
   462  	go func(conn net.Conn, ch chan bool) {
   463  		defer close(ch)
   464  
   465  		client := newTestClient(conn, false)
   466  		if data, err := client.receive(); err != nil {
   467  			t.Fatalf("failed to receive command: %v", err)
   468  		} else if data != "QStartNoAckMode" {
   469  			t.Errorf("unexpected data: %s", data)
   470  		}
   471  
   472  		if err := client.send("OK"); err != nil {
   473  			t.Fatalf("failed to receive command: %v", err)
   474  		}
   475  	}(connForSend, sendDone)
   476  
   477  	client := newTestClient(connForReceive, false)
   478  
   479  	if err := client.setNoAckMode(); err != nil {
   480  		t.Errorf("unexpected error: %v", err)
   481  	}
   482  	if !client.noAckMode {
   483  		t.Errorf("ack mode is not set")
   484  	}
   485  
   486  	<-sendDone
   487  }
   488  
   489  func TestSetNoAckMode_ErrorReturned(t *testing.T) {
   490  	connForReceive, connForSend := net.Pipe()
   491  
   492  	sendDone := make(chan bool)
   493  	go func(conn net.Conn, ch chan bool) {
   494  		defer close(ch)
   495  
   496  		client := newTestClient(conn, false)
   497  		_, _ = client.receive()
   498  		_ = client.send("E00")
   499  	}(connForSend, sendDone)
   500  
   501  	client := newTestClient(connForReceive, false)
   502  
   503  	if err := client.setNoAckMode(); err == nil {
   504  		t.Errorf("error is not returned")
   505  	}
   506  
   507  	<-sendDone
   508  }
   509  
   510  func TestQSupported(t *testing.T) {
   511  	connForReceive, connForSend := net.Pipe()
   512  
   513  	sendDone := make(chan bool)
   514  	go func(conn net.Conn, ch chan bool) {
   515  		defer close(ch)
   516  
   517  		client := newTestClient(conn, true)
   518  		if data, err := client.receive(); err != nil {
   519  			t.Fatalf("failed to receive command: %v", err)
   520  		} else if data != "qSupported:swbreak+;hwbreak+;no-resumed+" {
   521  			t.Errorf("unexpected data: %s", data)
   522  		}
   523  
   524  		if err := client.send("qXfer:features:read+;PacketSize=20000;qEcho+"); err != nil {
   525  			t.Fatalf("failed to send command: %v", err)
   526  		}
   527  	}(connForSend, sendDone)
   528  
   529  	client := newTestClient(connForReceive, true)
   530  
   531  	if err := client.qSupported(); err != nil {
   532  		t.Errorf("unexpected error: %v", err)
   533  	}
   534  
   535  	<-sendDone
   536  }
   537  
   538  func TestCollectRegisterMetadata(t *testing.T) {
   539  	connForReceive, connForSend := net.Pipe()
   540  
   541  	sendDone := make(chan bool)
   542  	go func(conn net.Conn, ch chan bool) {
   543  		defer close(ch)
   544  
   545  		client := newTestClient(conn, true)
   546  		_, _ = client.receive()
   547  		_ = client.send("name:rax;bitsize:64;offset:0;")
   548  		_, _ = client.receive()
   549  		_ = client.send("name:rbx;bitsize:64;offset:8;")
   550  		_, _ = client.receive()
   551  		_ = client.send("E45")
   552  
   553  	}(connForSend, sendDone)
   554  
   555  	client := newTestClient(connForReceive, true)
   556  
   557  	meatadata, err := client.collectRegisterMetadata()
   558  	if err != nil {
   559  		t.Fatalf("unexpected error: %v", err)
   560  	}
   561  	if len(meatadata) != 2 {
   562  		t.Errorf("wrong length of register metadata: %d", len(meatadata))
   563  	}
   564  
   565  	<-sendDone
   566  }
   567  
   568  func TestQRegisterInfo(t *testing.T) {
   569  	connForReceive, connForSend := net.Pipe()
   570  
   571  	sendDone := make(chan bool)
   572  	go func(conn net.Conn, ch chan bool) {
   573  		defer close(ch)
   574  
   575  		client := newTestClient(conn, true)
   576  		if data, err := client.receive(); err != nil {
   577  			t.Fatalf("failed to receive command: %v", err)
   578  		} else if data != "qRegisterInfo0" {
   579  			t.Errorf("unexpected data: %s", data)
   580  		}
   581  
   582  		if err := client.send("name:rax;bitsize:64;offset:0;encoding:uint;format:hex;set:General Purpose Registers;ehframe:0;dwarf:0;invalidate-regs:0,15,25,35,39;"); err != nil {
   583  			t.Fatalf("failed to send response: %v", err)
   584  		}
   585  	}(connForSend, sendDone)
   586  
   587  	client := newTestClient(connForReceive, true)
   588  
   589  	reg, err := client.qRegisterInfo(0)
   590  	if err != nil {
   591  		t.Fatalf("unexpected error: %v", err)
   592  	}
   593  	if reg.name != "rax" {
   594  		t.Errorf("wrong name: %s", reg.name)
   595  	}
   596  	if reg.offset != 0 {
   597  		t.Errorf("wrong offset: %d", reg.offset)
   598  	}
   599  	if reg.size != 8 {
   600  		t.Errorf("wrong size: %d", reg.size)
   601  	}
   602  
   603  	<-sendDone
   604  }
   605  
   606  func TestQRegisterInfo_EndOfRegisterList(t *testing.T) {
   607  	connForReceive, connForSend := net.Pipe()
   608  
   609  	sendDone := make(chan bool)
   610  	go func(conn net.Conn, ch chan bool) {
   611  		defer close(ch)
   612  
   613  		client := newTestClient(conn, true)
   614  		_, _ = client.receive()
   615  		_ = client.send("E45")
   616  	}(connForSend, sendDone)
   617  
   618  	client := newTestClient(connForReceive, true)
   619  
   620  	_, err := client.qRegisterInfo(0)
   621  	if err != errEndOfList {
   622  		t.Fatalf("unexpected error: %v", err)
   623  	}
   624  
   625  	<-sendDone
   626  }
   627  
   628  func TestQListThreadsInStopReply(t *testing.T) {
   629  	connForReceive, connForSend := net.Pipe()
   630  
   631  	sendDone := make(chan bool)
   632  	go func(conn net.Conn, ch chan bool) {
   633  		defer close(ch)
   634  
   635  		client := newTestClient(conn, true)
   636  		if data, err := client.receive(); err != nil {
   637  			t.Fatalf("failed to receive command: %v", err)
   638  		} else if data != "QListThreadsInStopReply" {
   639  			t.Errorf("unexpected data: %s", data)
   640  		}
   641  
   642  		if err := client.send("OK"); err != nil {
   643  			t.Fatalf("failed to send command: %v", err)
   644  		}
   645  	}(connForSend, sendDone)
   646  
   647  	client := newTestClient(connForReceive, true)
   648  
   649  	if err := client.qListThreadsInStopReply(); err != nil {
   650  		t.Errorf("unexpected error: %v", err)
   651  	}
   652  
   653  	<-sendDone
   654  }
   655  
   656  func TestQfThreadInfo(t *testing.T) {
   657  	connForReceive, connForSend := net.Pipe()
   658  
   659  	sendDone := make(chan bool)
   660  	go func(conn net.Conn, ch chan bool) {
   661  		defer close(ch)
   662  
   663  		client := newTestClient(conn, true)
   664  		if data, err := client.receive(); err != nil {
   665  			t.Fatalf("failed to receive command: %v", err)
   666  		} else if data != "qfThreadInfo" {
   667  			t.Errorf("unexpected data: %s", data)
   668  		}
   669  
   670  		if err := client.send("m15296fb"); err != nil {
   671  			t.Fatalf("failed to send command: %v", err)
   672  		}
   673  	}(connForSend, sendDone)
   674  
   675  	client := newTestClient(connForReceive, true)
   676  
   677  	threadID, err := client.qfThreadInfo()
   678  	if err != nil {
   679  		t.Errorf("unexpected error: %v", err)
   680  	}
   681  	if threadID != "15296fb" {
   682  		t.Errorf("unexpected threadID: %v", threadID)
   683  	}
   684  
   685  	<-sendDone
   686  }
   687  
   688  func TestSendAndReceive(t *testing.T) {
   689  	connForReceive, connForSend := net.Pipe()
   690  	cmd := "command"
   691  
   692  	sendDone := make(chan bool)
   693  	go func(conn net.Conn, ch chan bool) {
   694  		defer close(ch)
   695  
   696  		client := newTestClient(conn, false)
   697  		if err := client.send(cmd); err != nil {
   698  			t.Fatalf("failed to send command: %v", err)
   699  		}
   700  	}(connForSend, sendDone)
   701  
   702  	client := newTestClient(connForReceive, false)
   703  	buff, err := client.receive()
   704  	if err != nil {
   705  		t.Fatalf("unexpected error: %v", err)
   706  	}
   707  	if cmd != buff {
   708  		t.Errorf("receieved unexpected data: %v", buff)
   709  	}
   710  
   711  	<-sendDone
   712  }
   713  
   714  func TestSendAndReceive_NoAckMode(t *testing.T) {
   715  	connForReceive, connForSend := net.Pipe()
   716  	cmd := "command"
   717  
   718  	sendDone := make(chan bool)
   719  	go func(conn net.Conn, ch chan bool) {
   720  		defer close(ch)
   721  
   722  		client := newTestClient(conn, true)
   723  		if err := client.send(cmd); err != nil {
   724  			t.Fatalf("failed to send command: %v", err)
   725  		}
   726  	}(connForSend, sendDone)
   727  
   728  	client := newTestClient(connForReceive, true)
   729  	buff, err := client.receive()
   730  	if err != nil {
   731  		t.Fatalf("unexpected error: %v", err)
   732  	}
   733  	if cmd != buff {
   734  		t.Errorf("receieved unexpected data: %v", buff)
   735  	}
   736  
   737  	<-sendDone
   738  }
   739  
   740  func TestVerifyPacket(t *testing.T) {
   741  	for i, test := range []struct {
   742  		packet      string
   743  		expectError bool
   744  	}{
   745  		{packet: "$command#df", expectError: false},
   746  		{packet: "#command#df", expectError: true},
   747  		{packet: "$command$df", expectError: true},
   748  		{packet: "$command#00", expectError: true},
   749  	} {
   750  		actual := verifyPacket(test.packet)
   751  		if test.expectError && actual == nil {
   752  			t.Errorf("[%d] error not returned", i)
   753  		} else if !test.expectError && actual != nil {
   754  			t.Errorf("[%d] error returned: %v", i, actual)
   755  		}
   756  	}
   757  }
   758  
   759  func TestHexToUint64(t *testing.T) {
   760  	for i, test := range []struct {
   761  		hex          string
   762  		littleEndian bool
   763  		expected     uint64
   764  	}{
   765  		{hex: "00000001", littleEndian: false, expected: 1},
   766  		{hex: "00000102", littleEndian: false, expected: 258},
   767  		{hex: "02010000", littleEndian: true, expected: 258},
   768  	} {
   769  		actual, _ := hexToUint64(test.hex, test.littleEndian)
   770  		if test.expected != actual {
   771  			t.Errorf("[%d] not expected value: %d", i, actual)
   772  		}
   773  	}
   774  }
   775  
   776  func TestUint64ToHex(t *testing.T) {
   777  	for i, test := range []struct {
   778  		input        uint64
   779  		littleEndian bool
   780  		expected     string
   781  	}{
   782  		{input: 1, littleEndian: false, expected: "0000000000000001"},
   783  		{input: 258, littleEndian: false, expected: "0000000000000102"},
   784  		{input: 258, littleEndian: true, expected: "0201000000000000"},
   785  	} {
   786  		actual := uint64ToHex(test.input, test.littleEndian)
   787  		if test.expected != actual {
   788  			t.Errorf("[%d] not expected value: %s", i, actual)
   789  		}
   790  	}
   791  }
   792  
   793  func TestChecksum(t *testing.T) {
   794  	for i, data := range []struct {
   795  		input    []byte
   796  		expected uint8
   797  	}{
   798  		{input: []byte{0x1, 0x2}, expected: 3},
   799  		{input: []byte{0x7f, 0x80}, expected: 255},
   800  		{input: []byte{0x80, 0x80}, expected: 0},
   801  		{input: []byte{0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64}, expected: 0xdf},
   802  	} {
   803  		sum := calcChecksum(data.input)
   804  		if sum != data.expected {
   805  			t.Errorf("[%d] wrong checksum: %x", i, sum)
   806  		}
   807  	}
   808  }
   809  
   810  func newTestClient(conn net.Conn, noAckMode bool) *Client {
   811  	return &Client{conn: conn, noAckMode: noAckMode, buffer: make([]byte, maxPacketSize)}
   812  }