go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/exec/execmock/mock_test.go (about)

     1  // Copyright 2023 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package execmock
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  
    21  	"go.chromium.org/luci/common/exec"
    22  	"go.chromium.org/luci/common/exec/internal/execmockctx"
    23  	"go.chromium.org/luci/common/system/environ"
    24  
    25  	. "github.com/smartystreets/goconvey/convey"
    26  	. "go.chromium.org/luci/common/testing/assertions"
    27  )
    28  
    29  func TestFilter(t *testing.T) {
    30  	t.Parallel()
    31  
    32  	Convey(`filter`, t, func() {
    33  		Convey(`Args`, func() {
    34  			Convey(`valid`, func() {
    35  				e := filter{}.withArgs([]string{"hello", "/(there|you)/"})
    36  				So(e.matches(&execmockctx.MockCriteria{Args: []string{"hello", "there"}}), ShouldBeTrue)
    37  				So(e.matches(&execmockctx.MockCriteria{Args: []string{"hello", "you"}}), ShouldBeTrue)
    38  				So(e.matches(&execmockctx.MockCriteria{Args: []string{"hello", "sir"}}), ShouldBeFalse)
    39  			})
    40  
    41  			Convey(`invalid`, func() {
    42  				So(func() {
    43  					filter{}.withArgs([]string{"/*/"})
    44  				}, ShouldPanicLike, "invalid regexp")
    45  			})
    46  		})
    47  
    48  		Convey(`Env`, func() {
    49  			Convey(`valid`, func() {
    50  				Convey(`single`, func() {
    51  					e := filter{}.withEnv("SOMETHING", "/cool.beans/")
    52  					So(e.matches(&execmockctx.MockCriteria{Env: environ.New([]string{"SOMETHING=cool beans"})}), ShouldBeTrue)
    53  					So(e.matches(&execmockctx.MockCriteria{Env: environ.New([]string{"SOMETHING=cool?beans"})}), ShouldBeTrue)
    54  					So(e.matches(&execmockctx.MockCriteria{Env: environ.New([]string{"SOMETHING=cool pintos"})}), ShouldBeFalse)
    55  
    56  					Convey(`double`, func() {
    57  						e = e.withEnv("OTHER", "nerds")
    58  						So(e.matches(&execmockctx.MockCriteria{Env: environ.New([]string{"SOMETHING=cool beans"})}), ShouldBeFalse)
    59  						So(e.matches(&execmockctx.MockCriteria{Env: environ.New([]string{"SOMETHING=cool beans", "OTHER=nerds"})}), ShouldBeTrue)
    60  					})
    61  
    62  					Convey(`negative`, func() {
    63  						e = e.withEnv("OTHER", "!")
    64  						So(e.matches(&execmockctx.MockCriteria{Env: environ.New([]string{"SOMETHING=cool beans"})}), ShouldBeTrue)
    65  						So(e.matches(&execmockctx.MockCriteria{Env: environ.New([]string{"SOMETHING=cool beans", "OTHER=nerds"})}), ShouldBeFalse)
    66  					})
    67  				})
    68  			})
    69  
    70  			Convey(`invalid`, func() {
    71  				So(func() {
    72  					filter{}.withEnv("SOMETHING", "/*/")
    73  				}, ShouldPanicLike, "invalid regexp")
    74  			})
    75  		})
    76  
    77  		Convey(`Less`, func() {
    78  			// TODO: Test me
    79  		})
    80  	})
    81  }
    82  
    83  func TestContext(t *testing.T) {
    84  	t.Parallel()
    85  
    86  	Convey(`context`, t, func() {
    87  		ctx := Init(context.Background())
    88  
    89  		Convey(`uninitialized`, func() {
    90  			err := exec.Command(context.Background(), "echo", "hello").Run()
    91  			So(err, ShouldErrLike, execmockctx.ErrNoMatchingMock)
    92  			So(err, ShouldErrLike, "execmock.Init not called on context")
    93  		})
    94  
    95  		Convey(`zero`, func() {
    96  			err := exec.Command(ctx, "echo", "hello").Run()
    97  			So(err, ShouldErrLike, execmockctx.ErrNoMatchingMock)
    98  
    99  			misses := ResetState(ctx)
   100  			So(misses, ShouldHaveLength, 1)
   101  			So(misses[0].Args, ShouldResemble, []string{"echo", "hello"})
   102  		})
   103  
   104  		Convey(`single`, func() {
   105  			uses := Simple.Mock(ctx)
   106  
   107  			err := exec.Command(ctx, "echo", "hello").Run()
   108  			So(err, ShouldBeNil)
   109  
   110  			usages := uses.Snapshot()
   111  			So(usages, ShouldHaveLength, 1)
   112  			So(usages[0].Args, ShouldResemble, []string{"echo", "hello"})
   113  			So(usages[0].GetPID(), ShouldNotEqual, 0)
   114  		})
   115  
   116  		Convey(`multi`, func() {
   117  			generalUsage := Simple.Mock(ctx)
   118  			specificUsage := Simple.WithArgs("echo").Mock(ctx, SimpleInput{Stdout: "mocky mock"})
   119  
   120  			out, err := exec.Command(ctx, "echo", "hello").CombinedOutput()
   121  			So(err, ShouldBeNil)
   122  			So(out, ShouldResemble, []byte("mocky mock"))
   123  
   124  			So(generalUsage.Snapshot(), ShouldBeEmpty)
   125  
   126  			usages := specificUsage.Snapshot()
   127  			So(usages, ShouldHaveLength, 1)
   128  			So(usages[0].Args, ShouldResemble, []string{"echo", "hello"})
   129  			So(usages[0].GetPID(), ShouldNotEqual, 0)
   130  		})
   131  
   132  		Convey(`multi (limit)`, func() {
   133  			generalUsage := Simple.Mock(ctx)
   134  			specificUsage := Simple.WithArgs("echo").WithLimit(1).Mock(ctx, SimpleInput{Stdout: "mocky mock"})
   135  
   136  			// fully consumes specificUsage
   137  			out, err := exec.Command(ctx, "echo", "hello").CombinedOutput()
   138  			So(err, ShouldBeNil)
   139  			So(out, ShouldResemble, []byte("mocky mock"))
   140  
   141  			// falls into generalUsage mock
   142  			out, err = exec.Command(ctx, "echo", "hello").CombinedOutput()
   143  			So(err, ShouldBeNil)
   144  			So(out, ShouldResemble, []byte(""))
   145  
   146  			So(generalUsage.Snapshot(), ShouldHaveLength, 1)
   147  			So(specificUsage.Snapshot(), ShouldHaveLength, 1)
   148  		})
   149  	})
   150  }
   151  
   152  func TestGobName(t *testing.T) {
   153  	t.Parallel()
   154  
   155  	Convey(`gobName`, t, func() {
   156  		So(gobName(SimpleInput{}), ShouldResemble, "go.chromium.org/luci/common/exec/execmock.SimpleInput")
   157  		So(gobName(&SimpleInput{}), ShouldResemble, "*go.chromium.org/luci/common/exec/execmock.SimpleInput")
   158  
   159  		So(gobName(100), ShouldResemble, "int")
   160  	})
   161  }