blob: e5e52fc96dd117e6719c4eb12101b9797017087d [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"bytes"
"chromiumos/test/dut/cmd/dutserver/dutssh"
"context"
"errors"
"io"
"log"
"net"
"testing"
"go.chromium.org/chromiumos/config/go/test/api"
"golang.org/x/crypto/ssh"
"google.golang.org/grpc"
)
type MockSession_Success struct {
Stdout io.Writer
Stderr io.Writer
}
func (s *MockSession_Success) Close() error {
return nil
}
func (s *MockSession_Success) SetStdout(writer io.Writer) {
s.Stdout = writer
}
func (s *MockSession_Success) SetStderr(writer io.Writer) {
s.Stderr = writer
}
func (s *MockSession_Success) Run(cmd string) error {
s.Stdout.Write([]byte("success!"))
s.Stderr.Write([]byte("not failed!"))
return nil
}
type MockConnection_Success struct{}
func (c *MockConnection_Success) Close() error {
return nil
}
func (c *MockConnection_Success) NewSession() (dutssh.SessionInterface, error) {
return &MockSession_Success{}, nil
}
// Tests if DutServiceServer can handle empty request without problem.
func TestDutServiceServer_Empty(t *testing.T) {
var logBuf bytes.Buffer
l, err := net.Listen("tcp", ":0")
if err != nil {
t.Fatal("Failed to create a net listener: ", err)
}
ctx := context.Background()
dutConn := &MockConnection_Success{}
srv := newDutServiceServer(l, log.New(&logBuf, "", log.LstdFlags|log.LUTC), dutConn)
if err != nil {
t.Fatalf("Failed to start DutServiceServer: %v", err)
}
go srv.Serve(l)
defer srv.Stop()
conn, err := grpc.Dial(l.Addr().String(), grpc.WithInsecure())
if err != nil {
t.Fatalf("Failed to dial: %v", err)
}
defer conn.Close()
cl := api.NewDutServiceClient(conn)
if _, err := cl.ExecCommand(ctx, &api.ExecCommandRequest{}); err != nil {
t.Fatalf("Failed at api.ExecCommand: %v", err)
}
}
// Tests that a command executes successfully
func TestDutServiceServer_CommandWorks(t *testing.T) {
var logBuf bytes.Buffer
l, err := net.Listen("tcp", ":0")
if err != nil {
t.Fatal("Failed to create a net listener: ", err)
}
ctx := context.Background()
dutConn := &MockConnection_Success{}
srv := newDutServiceServer(l, log.New(&logBuf, "", log.LstdFlags|log.LUTC), dutConn)
if err != nil {
t.Fatalf("Failed to start DutServiceServer: %v", err)
}
go srv.Serve(l)
defer srv.Stop()
conn, err := grpc.Dial(l.Addr().String(), grpc.WithInsecure())
if err != nil {
t.Fatalf("Failed to dial: %v", err)
}
defer conn.Close()
cl := api.NewDutServiceClient(conn)
stream, err := cl.ExecCommand(ctx, &api.ExecCommandRequest{})
if err != nil {
t.Fatalf("Failed at api.ExecCommand: %v", err)
}
resp := &api.ExecCommandResponse{}
err = stream.RecvMsg(resp)
if err != nil {
t.Fatalf("Failed at api.ExecCommand: %v", err)
}
if resp.ExitInfo.Status != 0 {
t.Fatalf("Expecting return type to be 0, instead got: %v", resp.ExitInfo.Status)
}
if string(resp.Stderr) != "not failed!" {
t.Fatalf("Expecting stderr to be \"not failed\", instead got %v", string(resp.Stderr))
}
if string(resp.Stdout) != "success!" {
t.Fatalf("Expecting stderr to be \"success\", instead got %v", string(resp.Stderr))
}
if resp.ExitInfo.Signaled {
t.Fatalf("Signalled should not be set!")
}
if !resp.ExitInfo.Started {
t.Fatalf("Started should be set!")
}
}
type MockSession_CommandFailed struct {
Stdout io.Writer
Stderr io.Writer
}
func (s *MockSession_CommandFailed) Close() error {
return nil
}
func (s *MockSession_CommandFailed) SetStdout(writer io.Writer) {
s.Stdout = writer
}
func (s *MockSession_CommandFailed) SetStderr(writer io.Writer) {
s.Stderr = writer
}
func (s *MockSession_CommandFailed) Run(cmd string) error {
s.Stdout.Write([]byte("not success!"))
s.Stderr.Write([]byte("failure!"))
wm := ssh.Waitmsg{}
return &ssh.ExitError{
Waitmsg: wm,
}
}
type MockConnection_CommandFailed struct{}
func (c *MockConnection_CommandFailed) Close() error {
return nil
}
func (c *MockConnection_CommandFailed) NewSession() (dutssh.SessionInterface, error) {
return &MockSession_CommandFailed{}, nil
}
// Tests that a command does not execute successfully
func TestDutServiceServer_CommandFails(t *testing.T) {
var logBuf bytes.Buffer
l, err := net.Listen("tcp", ":0")
if err != nil {
t.Fatal("Failed to create a net listener: ", err)
}
ctx := context.Background()
dutConn := &MockConnection_CommandFailed{}
srv := newDutServiceServer(l, log.New(&logBuf, "", log.LstdFlags|log.LUTC), dutConn)
if err != nil {
t.Fatalf("Failed to start DutServiceServer: %v", err)
}
go srv.Serve(l)
defer srv.Stop()
conn, err := grpc.Dial(l.Addr().String(), grpc.WithInsecure())
if err != nil {
t.Fatalf("Failed to dial: %v", err)
}
defer conn.Close()
cl := api.NewDutServiceClient(conn)
stream, err := cl.ExecCommand(ctx, &api.ExecCommandRequest{})
if err != nil {
t.Fatalf("Failed at api.ExecCommand: %v", err)
}
resp := &api.ExecCommandResponse{}
err = stream.RecvMsg(resp)
if err != nil {
t.Fatalf("Failed at api.ExecCommand: %v", err)
}
if string(resp.Stderr) != "failure!" {
t.Fatalf("Expecting stderr to be \"not success\", instead got %v", string(resp.Stderr))
}
if string(resp.Stdout) != "not success!" {
t.Fatalf("Expecting stderr to be \"failure\", instead got %v", string(resp.Stderr))
}
if !resp.ExitInfo.Signaled {
t.Fatalf("Signalled should be set!")
}
if !resp.ExitInfo.Started {
t.Fatalf("Started should be set!")
}
}
type MockSession_PreCommandFailure struct {
Stdout io.Writer
Stderr io.Writer
}
func (s *MockSession_PreCommandFailure) Close() error {
return nil
}
func (s *MockSession_PreCommandFailure) SetStdout(writer io.Writer) {
s.Stdout = writer
}
func (s *MockSession_PreCommandFailure) SetStderr(writer io.Writer) {
s.Stderr = writer
}
func (s *MockSession_PreCommandFailure) Run(cmd string) error {
s.Stdout.Write([]byte(""))
s.Stderr.Write([]byte(""))
return &ssh.ExitMissingError{}
}
type MockConnection_PreCommandFailure struct{}
func (c *MockConnection_PreCommandFailure) Close() error {
return nil
}
func (c *MockConnection_PreCommandFailure) NewSession() (dutssh.SessionInterface, error) {
return &MockSession_PreCommandFailure{}, nil
}
// Tests that a command does not execute
func TestDutServiceServer_PreCommandFails(t *testing.T) {
var logBuf bytes.Buffer
l, err := net.Listen("tcp", ":0")
if err != nil {
t.Fatal("Failed to create a net listener: ", err)
}
ctx := context.Background()
dutConn := &MockConnection_PreCommandFailure{}
srv := newDutServiceServer(l, log.New(&logBuf, "", log.LstdFlags|log.LUTC), dutConn)
if err != nil {
t.Fatalf("Failed to start DutServiceServer: %v", err)
}
go srv.Serve(l)
defer srv.Stop()
conn, err := grpc.Dial(l.Addr().String(), grpc.WithInsecure())
if err != nil {
t.Fatalf("Failed to dial: %v", err)
}
defer conn.Close()
cl := api.NewDutServiceClient(conn)
stream, err := cl.ExecCommand(ctx, &api.ExecCommandRequest{})
if err != nil {
t.Fatalf("Failed at api.ExecCommand: %v", err)
}
resp := &api.ExecCommandResponse{}
err = stream.RecvMsg(resp)
if err != nil {
t.Fatalf("Failed at api.ExecCommand: %v", err)
}
if string(resp.Stderr) != "" {
t.Fatalf("Expecting stderr to be empty, instead got %v", string(resp.Stderr))
}
if string(resp.Stdout) != "" {
t.Fatalf("Expecting stderr to be empty, instead got %v", string(resp.Stderr))
}
if resp.ExitInfo.Signaled {
t.Fatalf("Signalled should not be set!")
}
if resp.ExitInfo.Started {
t.Fatalf("Started should not be set!")
}
}
type MockConnection_NewSessionFailure struct{}
func (c *MockConnection_NewSessionFailure) Close() error {
return nil
}
func (c *MockConnection_NewSessionFailure) NewSession() (dutssh.SessionInterface, error) {
return nil, errors.New("Session failed.")
}
// Tests that a session fails
func TestDutServiceServer_NewSessionFails(t *testing.T) {
var logBuf bytes.Buffer
l, err := net.Listen("tcp", ":0")
if err != nil {
t.Fatal("Failed to create a net listener: ", err)
}
ctx := context.Background()
dutConn := &MockConnection_NewSessionFailure{}
srv := newDutServiceServer(l, log.New(&logBuf, "", log.LstdFlags|log.LUTC), dutConn)
if err != nil {
t.Fatalf("Failed to start DutServiceServer: %v", err)
}
go srv.Serve(l)
defer srv.Stop()
conn, err := grpc.Dial(l.Addr().String(), grpc.WithInsecure())
if err != nil {
t.Fatalf("Failed to dial: %v", err)
}
defer conn.Close()
cl := api.NewDutServiceClient(conn)
stream, err := cl.ExecCommand(ctx, &api.ExecCommandRequest{})
if err != nil {
t.Fatalf("Failed at api.ExecCommand: %v", err)
}
resp := &api.ExecCommandResponse{}
err = stream.RecvMsg(resp)
if err != nil {
t.Fatalf("Failed at api.ExecCommand: %v", err)
}
if string(resp.Stderr) != "" {
t.Fatalf("Expecting stderr to be empty, instead got %v", string(resp.Stderr))
}
if string(resp.Stdout) != "" {
t.Fatalf("Expecting stderr to be empty, instead got %v", string(resp.Stderr))
}
if resp.ExitInfo.Signaled {
t.Fatalf("Signalled should not be set!")
}
if resp.ExitInfo.Started {
t.Fatalf("Started should not be set!")
}
if resp.ExitInfo.ErrorMessage != "Session failed." {
t.Fatalf("Error message should be session failed, instead got %v", resp.ExitInfo.ErrorMessage)
}
}