github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/p2p/discv5/sim_run_test.go (about)

     1  // Copyright 2016 The Spectrum Authors
     2  // This file is part of the Spectrum library.
     3  //
     4  // The Spectrum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The Spectrum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the Spectrum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package discv5
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"encoding/binary"
    23  	"errors"
    24  	"fmt"
    25  	"io"
    26  	"os"
    27  	"os/exec"
    28  	"runtime"
    29  	"strings"
    30  	"testing"
    31  )
    32  
    33  func getnacl() (string, error) {
    34  	switch runtime.GOARCH {
    35  	case "amd64":
    36  		_, err := exec.LookPath("sel_ldr_x86_64")
    37  		return "amd64p32", err
    38  	case "i386":
    39  		_, err := exec.LookPath("sel_ldr_i386")
    40  		return "i386", err
    41  	default:
    42  		return "", errors.New("nacl is not supported on " + runtime.GOARCH)
    43  	}
    44  }
    45  
    46  // runWithPlaygroundTime executes the caller
    47  // in the NaCl sandbox with faketime enabled.
    48  //
    49  // This function must be called from a Test* function
    50  // and the caller must skip the actual test when isHost is true.
    51  func runWithPlaygroundTime(t *testing.T) (isHost bool) {
    52  	if runtime.GOOS == "nacl" {
    53  		return false
    54  	}
    55  
    56  	// Get the caller.
    57  	callerPC, _, _, ok := runtime.Caller(1)
    58  	if !ok {
    59  		panic("can't get caller")
    60  	}
    61  	callerFunc := runtime.FuncForPC(callerPC)
    62  	if callerFunc == nil {
    63  		panic("can't get caller")
    64  	}
    65  	callerName := callerFunc.Name()[strings.LastIndexByte(callerFunc.Name(), '.')+1:]
    66  	if !strings.HasPrefix(callerName, "Test") {
    67  		panic("must be called from witin a Test* function")
    68  	}
    69  	testPattern := "^" + callerName + "$"
    70  
    71  	// Unfortunately runtime.faketime (playground time mode) only works on NaCl. The NaCl
    72  	// SDK must be installed and linked into PATH for this to work.
    73  	arch, err := getnacl()
    74  	if err != nil {
    75  		t.Skip(err)
    76  	}
    77  
    78  	// Compile and run the calling test using NaCl.
    79  	// The extra tag ensures that the TestMain function in sim_main_test.go is used.
    80  	cmd := exec.Command("go", "test", "-v", "-tags", "faketime_simulation", "-timeout", "100h", "-run", testPattern, ".")
    81  	cmd.Env = append([]string{"GOOS=nacl", "GOARCH=" + arch}, os.Environ()...)
    82  	stdout, _ := cmd.StdoutPipe()
    83  	stderr, _ := cmd.StderrPipe()
    84  	go skipPlaygroundOutputHeaders(os.Stdout, stdout)
    85  	go skipPlaygroundOutputHeaders(os.Stderr, stderr)
    86  	if err := cmd.Run(); err != nil {
    87  		t.Error(err)
    88  	}
    89  
    90  	// Ensure that the test function doesn't run in the (non-NaCl) host process.
    91  	return true
    92  }
    93  
    94  func skipPlaygroundOutputHeaders(out io.Writer, in io.Reader) {
    95  	// Additional output can be printed without the headers
    96  	// before the NaCl binary starts running (e.g. compiler error messages).
    97  	bufin := bufio.NewReader(in)
    98  	output, err := bufin.ReadBytes(0)
    99  	output = bytes.TrimSuffix(output, []byte{0})
   100  	if len(output) > 0 {
   101  		out.Write(output)
   102  	}
   103  	if err != nil {
   104  		return
   105  	}
   106  	bufin.UnreadByte()
   107  
   108  	// Playback header: 0 0 P B <8-byte time> <4-byte data length>
   109  	head := make([]byte, 4+8+4)
   110  	for {
   111  		if _, err := io.ReadFull(bufin, head); err != nil {
   112  			if err != io.EOF {
   113  				fmt.Fprintln(out, "read error:", err)
   114  			}
   115  			return
   116  		}
   117  		if !bytes.HasPrefix(head, []byte{0x00, 0x00, 'P', 'B'}) {
   118  			fmt.Fprintf(out, "expected playback header, got %q\n", head)
   119  			io.Copy(out, bufin)
   120  			return
   121  		}
   122  		// Copy data until next header.
   123  		size := binary.BigEndian.Uint32(head[12:])
   124  		io.CopyN(out, bufin, int64(size))
   125  	}
   126  }