Add RBE metrics dump in Soong UI.

From aosp/1329396, the RBE metrics protobuf file is part of the
metrics uploading process. The RBE metrics protobuf file is
generated by running the bootstrap shutdown command. A new function
named DumpRBEMetrics was written in order to generate the RBE metrics
protobuf file before sending to the uploading process.

Bug: b/140638454
Test: * Unit test cases
      * Ran RBE build on my local host and verified the
        metrics protobuf file is created.
      * Ran non-RBE build after RBE build and verified that
        the previous metrics protobuf file was deleted.

Change-Id: I4b8068905cb67c4b8c2d94793917b98974fed707
diff --git a/ui/build/rbe_test.go b/ui/build/rbe_test.go
new file mode 100644
index 0000000..2c4995b
--- /dev/null
+++ b/ui/build/rbe_test.go
@@ -0,0 +1,142 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+
+	"android/soong/ui/logger"
+)
+
+func TestDumpRBEMetrics(t *testing.T) {
+	ctx := testContext()
+	tests := []struct {
+		description string
+		env         []string
+		generated   bool
+	}{{
+		description: "RBE disabled",
+		env: []string{
+			"NOSTART_RBE=true",
+		},
+	}, {
+		description: "rbe metrics generated",
+		env: []string{
+			"USE_RBE=true",
+		},
+		generated: true,
+	}}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			tmpDir := t.TempDir()
+
+			rbeBootstrapCmd := filepath.Join(tmpDir, bootstrapCmd)
+			if err := ioutil.WriteFile(rbeBootstrapCmd, []byte(rbeBootstrapProgram), 0755); err != nil {
+				t.Fatalf("failed to create a fake bootstrap command file %s: %v", rbeBootstrapCmd, err)
+			}
+
+			env := Environment(tt.env)
+			env.Set("OUT_DIR", tmpDir)
+			env.Set("RBE_DIR", tmpDir)
+			env.Set("RBE_output_dir", t.TempDir())
+			config := Config{&configImpl{
+				environ: &env,
+			}}
+
+			rbeMetricsFilename := filepath.Join(tmpDir, rbeMetricsPBFilename)
+			DumpRBEMetrics(ctx, config, rbeMetricsFilename)
+
+			// Validate that the rbe metrics file exists if RBE is enabled.
+			if _, err := os.Stat(rbeMetricsFilename); err == nil {
+				if !tt.generated {
+					t.Errorf("got true, want false for rbe metrics file %s to exist.", rbeMetricsFilename)
+				}
+			} else if os.IsNotExist(err) {
+				if tt.generated {
+					t.Errorf("got false, want true for rbe metrics file %s to exist.", rbeMetricsFilename)
+				}
+			} else {
+				t.Errorf("unknown error found on checking %s exists: %v", rbeMetricsFilename, err)
+			}
+		})
+	}
+}
+
+func TestDumpRBEMetricsErrors(t *testing.T) {
+	ctx := testContext()
+	tests := []struct {
+		description         string
+		rbeOutputDirDefined bool
+		bootstrapProgram    string
+		expectedErr         string
+	}{{
+		description:      "output_dir not defined",
+		bootstrapProgram: rbeBootstrapProgram,
+		expectedErr:      "RBE output dir variable not defined",
+	}, {
+		description:         "stopRBE failed",
+		rbeOutputDirDefined: true,
+		bootstrapProgram:    "#!/bin/bash\nexit 1",
+		expectedErr:         "shutdown failed",
+	}, {
+		description:         "failed to copy metrics file",
+		rbeOutputDirDefined: true,
+		bootstrapProgram:    "#!/bin/bash",
+		expectedErr:         "failed to copy",
+	}}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			defer logger.Recover(func(err error) {
+				got := err.Error()
+				if !strings.Contains(got, tt.expectedErr) {
+					t.Errorf("got %q, want %q to be contained in error", got, tt.expectedErr)
+				}
+			})
+
+			tmpDir := t.TempDir()
+
+			rbeBootstrapCmd := filepath.Join(tmpDir, bootstrapCmd)
+			if err := ioutil.WriteFile(rbeBootstrapCmd, []byte(tt.bootstrapProgram), 0755); err != nil {
+				t.Fatalf("failed to create a fake bootstrap command file %s: %v", rbeBootstrapCmd, err)
+			}
+
+			env := &Environment{}
+			env.Set("USE_RBE", "true")
+			env.Set("OUT_DIR", tmpDir)
+			env.Set("RBE_DIR", tmpDir)
+
+			if tt.rbeOutputDirDefined {
+				env.Set("RBE_output_dir", t.TempDir())
+			}
+
+			config := Config{&configImpl{
+				environ: env,
+			}}
+
+			rbeMetricsFilename := filepath.Join(tmpDir, rbeMetricsPBFilename)
+			DumpRBEMetrics(ctx, config, rbeMetricsFilename)
+			t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
+		})
+	}
+}
+
+var rbeBootstrapProgram = fmt.Sprintf("#!/bin/bash\necho 1 > $RBE_output_dir/%s", rbeMetricsPBFilename)