blob: 8580d4aaa379398354c118cb54782e15d1ac332b [file] [log] [blame]
// Copyright 2019 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"
"flag"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
// Attention: The tests in this file execute the test binary again with the `-run` flag.
// This is needed as they want to test an `exec`, which terminates the test process.
var internalexececho = flag.Bool("internalexececho", false, "internal flag used for tests that exec")
func TestProcessEnvExecPathAndArgs(t *testing.T) {
withTestContext(t, func(ctx *testContext) {
if *internalexececho {
execEcho(ctx, &command{
Path: "some_binary",
Args: []string{"arg1", "arg2"},
})
return
}
logLines := forkAndReadEcho(ctx)
if !strings.HasSuffix(logLines[0], "/some_binary arg1 arg2") {
t.Errorf("incorrect path or args: %s", logLines[0])
}
})
}
func TestProcessEnvExecAddEnv(t *testing.T) {
withTestContext(t, func(ctx *testContext) {
if *internalexececho {
execEcho(ctx, &command{
Path: "some_binary",
EnvUpdates: []string{"ABC=xyz"},
})
return
}
logLines := forkAndReadEcho(ctx)
for _, ll := range logLines {
if ll == "ABC=xyz" {
return
}
}
t.Errorf("could not find new env variable: %s", logLines)
})
}
func TestProcessEnvExecUpdateEnv(t *testing.T) {
if os.Getenv("PATH") == "" {
t.Fatal("no PATH environment variable found!")
}
withTestContext(t, func(ctx *testContext) {
if *internalexececho {
execEcho(ctx, &command{
Path: "some_binary",
EnvUpdates: []string{"PATH=xyz"},
})
return
}
logLines := forkAndReadEcho(ctx)
for _, ll := range logLines {
if ll == "PATH=xyz" {
return
}
}
t.Errorf("could not find updated env variable: %s", logLines)
})
}
func TestProcessEnvExecDeleteEnv(t *testing.T) {
if os.Getenv("PATH") == "" {
t.Fatal("no PATH environment variable found!")
}
withTestContext(t, func(ctx *testContext) {
if *internalexececho {
execEcho(ctx, &command{
Path: "some_binary",
EnvUpdates: []string{"PATH="},
})
return
}
logLines := forkAndReadEcho(ctx)
for _, ll := range logLines {
if strings.HasPrefix(ll, "PATH=") {
t.Errorf("path env was not removed: %s", ll)
}
}
})
}
func TestProcessEnvRunCmdPathAndArgs(t *testing.T) {
withTestContext(t, func(ctx *testContext) {
cmd := &command{
Path: "some_binary",
Args: []string{"arg1", "arg2"},
}
logLines := runAndEcho(ctx, cmd)
if !strings.HasSuffix(logLines[0], "/some_binary arg1 arg2") {
t.Errorf("incorrect path or args: %s", logLines[0])
}
})
}
func TestProcessEnvRunCmdAddEnv(t *testing.T) {
withTestContext(t, func(ctx *testContext) {
cmd := &command{
Path: "some_binary",
EnvUpdates: []string{"ABC=xyz"},
}
logLines := runAndEcho(ctx, cmd)
for _, ll := range logLines {
if ll == "ABC=xyz" {
return
}
}
t.Errorf("could not find new env variable: %s", logLines)
})
}
func TestProcessEnvRunCmdUpdateEnv(t *testing.T) {
withTestContext(t, func(ctx *testContext) {
if os.Getenv("PATH") == "" {
t.Fatal("no PATH environment variable found!")
}
cmd := &command{
Path: "some_binary",
EnvUpdates: []string{"PATH=xyz"},
}
logLines := runAndEcho(ctx, cmd)
for _, ll := range logLines {
if ll == "PATH=xyz" {
return
}
}
t.Errorf("could not find updated env variable: %s", logLines)
})
}
func TestProcessEnvRunCmdDeleteEnv(t *testing.T) {
withTestContext(t, func(ctx *testContext) {
if os.Getenv("PATH") == "" {
t.Fatal("no PATH environment variable found!")
}
cmd := &command{
Path: "some_binary",
EnvUpdates: []string{"PATH="},
}
logLines := runAndEcho(ctx, cmd)
for _, ll := range logLines {
if strings.HasPrefix(ll, "PATH=") {
t.Errorf("path env was not removed: %s", ll)
}
}
})
}
func execEcho(ctx *testContext, cmd *command) {
env := &processEnv{}
err := env.exec(createEcho(ctx, cmd))
if err != nil {
os.Stderr.WriteString(err.Error())
}
os.Exit(1)
}
func forkAndReadEcho(ctx *testContext) []string {
testBin, err := os.Executable()
if err != nil {
ctx.t.Fatalf("unable to read the executable: %s", err)
}
subCmd := exec.Command(testBin, "-internalexececho", "-test.run="+ctx.t.Name())
output, err := subCmd.CombinedOutput()
if err != nil {
ctx.t.Fatalf("error calling test binary again for exec: %s", err)
}
return strings.Split(string(output), "\n")
}
func runAndEcho(ctx *testContext, cmd *command) []string {
env, err := newProcessEnv()
if err != nil {
ctx.t.Fatalf("creation of process env failed: %s", err)
}
buffer := bytes.Buffer{}
if err := env.run(createEcho(ctx, cmd), nil, &buffer, &buffer); err != nil {
ctx.t.Fatalf("run failed: %s", err)
}
return strings.Split(buffer.String(), "\n")
}
func createEcho(ctx *testContext, cmd *command) *command {
content := `
/bin/echo "$0" "$@"
/usr/bin/env
`
fullPath := filepath.Join(ctx.tempDir, cmd.Path)
ctx.writeFile(fullPath, content)
// Note: Using a self executable wrapper does not work due to a race condition
// on unix systems. See https://github.com/golang/go/issues/22315
return &command{
Path: "bash",
Args: append([]string{fullPath}, cmd.Args...),
EnvUpdates: cmd.EnvUpdates,
}
}