Merge remote-tracking branch 'aosp/upstream' into master

*   e8a09f9 Merge pull request #141 from colincross/fs
|\
| * b519a7e Add globbing to filesystem mocking
|/
* 2eeb7ee Merge pull request #140 from danw/sort_tests
* 61caa24 Sort testcases to make build (more) reproducible

Test: soong tests
diff --git a/Blueprints b/Blueprints
index 702d0de..582a0aa 100644
--- a/Blueprints
+++ b/Blueprints
@@ -8,7 +8,6 @@
     pkgPath = "github.com/google/blueprint",
     srcs = [
         "context.go",
-        "fs.go",
         "glob.go",
         "live_tracker.go",
         "mangle.go",
@@ -61,6 +60,7 @@
     ],
     srcs = [
         "pathtools/lists.go",
+        "pathtools/fs.go",
         "pathtools/glob.go",
     ],
     testSrcs = [
diff --git a/build.ninja.in b/build.ninja.in
index ec32fcf..1de3dbb 100644
--- a/build.ninja.in
+++ b/build.ninja.in
@@ -58,9 +58,8 @@
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
         : g.bootstrap.compile ${g.bootstrap.srcDir}/context.go $
-        ${g.bootstrap.srcDir}/fs.go ${g.bootstrap.srcDir}/glob.go $
-        ${g.bootstrap.srcDir}/live_tracker.go ${g.bootstrap.srcDir}/mangle.go $
-        ${g.bootstrap.srcDir}/module_ctx.go $
+        ${g.bootstrap.srcDir}/glob.go ${g.bootstrap.srcDir}/live_tracker.go $
+        ${g.bootstrap.srcDir}/mangle.go ${g.bootstrap.srcDir}/module_ctx.go $
         ${g.bootstrap.srcDir}/ninja_defs.go $
         ${g.bootstrap.srcDir}/ninja_strings.go $
         ${g.bootstrap.srcDir}/ninja_writer.go $
@@ -130,7 +129,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
-# Defined: Blueprints:50:1
+# Defined: Blueprints:49:1
 
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
@@ -145,7 +144,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
-# Defined: Blueprints:34:1
+# Defined: Blueprints:33:1
 
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
@@ -163,11 +162,12 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
-# Defined: Blueprints:56:1
+# Defined: Blueprints:55:1
 
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
         : g.bootstrap.compile ${g.bootstrap.srcDir}/pathtools/lists.go $
+        ${g.bootstrap.srcDir}/pathtools/fs.go $
         ${g.bootstrap.srcDir}/pathtools/glob.go | ${g.bootstrap.compileCmd} $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a
     incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg
diff --git a/context.go b/context.go
index c28215d..f63b1af 100644
--- a/context.go
+++ b/context.go
@@ -31,6 +31,7 @@
 	"text/template"
 
 	"github.com/google/blueprint/parser"
+	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -104,7 +105,7 @@
 	globs    map[string]GlobPath
 	globLock sync.Mutex
 
-	fs fileSystem
+	fs pathtools.FileSystem
 }
 
 // An Error describes a problem that was encountered that is related to a
@@ -270,7 +271,7 @@
 		moduleInfo:       make(map[Module]*moduleInfo),
 		moduleNinjaNames: make(map[string]*moduleGroup),
 		globs:            make(map[string]GlobPath),
-		fs:               fs,
+		fs:               pathtools.OsFs,
 	}
 
 	ctx.RegisterBottomUpMutator("blueprint_deps", blueprintDepsMutator)
@@ -768,9 +769,7 @@
 // MockFileSystem causes the Context to replace all reads with accesses to the provided map of
 // filenames to contents stored as a byte slice.
 func (c *Context) MockFileSystem(files map[string][]byte) {
-	c.fs = &mockFS{
-		files: files,
-	}
+	c.fs = pathtools.MockFs(files)
 }
 
 // parseBlueprintFile parses a single Blueprints file, returning any errors through
diff --git a/fs.go b/fs.go
deleted file mode 100644
index 653d6d6..0000000
--- a/fs.go
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2016 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 blueprint
-
-import (
-	"bytes"
-	"io"
-	"io/ioutil"
-	"os"
-)
-
-// Based on Andrew Gerrand's "10 things you (probably) dont' know about Go"
-
-var fs fileSystem = osFS{}
-
-type fileSystem interface {
-	Open(name string) (io.ReadCloser, error)
-	Exists(name string) (bool, bool, error)
-}
-
-// osFS implements fileSystem using the local disk.
-type osFS struct{}
-
-func (osFS) Open(name string) (io.ReadCloser, error) { return os.Open(name) }
-func (osFS) Exists(name string) (bool, bool, error) {
-	stat, err := os.Stat(name)
-	if err == nil {
-		return true, stat.IsDir(), nil
-	} else if os.IsNotExist(err) {
-		return false, false, nil
-	} else {
-		return false, false, err
-	}
-}
-
-type mockFS struct {
-	files map[string][]byte
-}
-
-func (m mockFS) Open(name string) (io.ReadCloser, error) {
-	if f, ok := m.files[name]; ok {
-		return struct {
-			io.Closer
-			*bytes.Reader
-		}{
-			ioutil.NopCloser(nil),
-			bytes.NewReader(f),
-		}, nil
-	}
-
-	return nil, &os.PathError{
-		Op:   "open",
-		Path: name,
-		Err:  os.ErrNotExist,
-	}
-}
-
-func (m mockFS) Exists(name string) (bool, bool, error) {
-	_, ok := m.files[name]
-	return ok, false, nil
-}
diff --git a/glob.go b/glob.go
index dad5edf..3497236 100644
--- a/glob.go
+++ b/glob.go
@@ -18,8 +18,6 @@
 	"fmt"
 	"reflect"
 	"sort"
-
-	"github.com/google/blueprint/pathtools"
 )
 
 type GlobPath struct {
@@ -54,7 +52,7 @@
 	}
 
 	// Get a globbed file list
-	files, deps, err := pathtools.GlobWithExcludes(pattern, excludes)
+	files, deps, err := c.fs.Glob(pattern, excludes)
 	if err != nil {
 		return nil, err
 	}
diff --git a/gotestmain/gotestmain.go b/gotestmain/gotestmain.go
index ff4a5ba..8d1287c 100644
--- a/gotestmain/gotestmain.go
+++ b/gotestmain/gotestmain.go
@@ -23,6 +23,7 @@
 	"go/token"
 	"io/ioutil"
 	"os"
+	"sort"
 	"strings"
 	"text/template"
 )
@@ -51,6 +52,7 @@
 			tests = append(tests, obj.Name)
 		}
 	}
+	sort.Strings(tests)
 	return
 }
 
diff --git a/module_ctx.go b/module_ctx.go
index abd87c1..a2fd9c9 100644
--- a/module_ctx.go
+++ b/module_ctx.go
@@ -18,6 +18,8 @@
 	"fmt"
 	"path/filepath"
 	"text/scanner"
+
+	"github.com/google/blueprint/pathtools"
 )
 
 // A Module handles generating all of the Ninja build actions needed to build a
@@ -134,6 +136,8 @@
 	// file that does not match the pattern is added to a searched directory.
 	GlobWithDeps(pattern string, excludes []string) ([]string, error)
 
+	Fs() pathtools.FileSystem
+
 	moduleInfo() *moduleInfo
 	error(err error)
 }
@@ -260,6 +264,10 @@
 	return d.context.glob(pattern, excludes)
 }
 
+func (d *baseModuleContext) Fs() pathtools.FileSystem {
+	return d.context.fs
+}
+
 var _ ModuleContext = (*moduleContext)(nil)
 
 type moduleContext struct {
diff --git a/pathtools/fs.go b/pathtools/fs.go
new file mode 100644
index 0000000..4ed91ff
--- /dev/null
+++ b/pathtools/fs.go
@@ -0,0 +1,151 @@
+// Copyright 2016 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 pathtools
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+)
+
+// Based on Andrew Gerrand's "10 things you (probably) dont' know about Go"
+
+var OsFs FileSystem = osFs{}
+
+func MockFs(files map[string][]byte) FileSystem {
+	fs := &mockFs{
+		files: make(map[string][]byte, len(files)),
+		dirs:  make(map[string]bool),
+		all:   []string(nil),
+	}
+
+	for f, b := range files {
+		fs.files[filepath.Clean(f)] = b
+		dir := filepath.Dir(f)
+		for dir != "." && dir != "/" {
+			fs.dirs[dir] = true
+			dir = filepath.Dir(dir)
+		}
+	}
+
+	for f := range fs.files {
+		fs.all = append(fs.all, f)
+	}
+
+	for d := range fs.dirs {
+		fs.all = append(fs.all, d)
+	}
+
+	return fs
+}
+
+type FileSystem interface {
+	Open(name string) (io.ReadCloser, error)
+	Exists(name string) (bool, bool, error)
+	Glob(pattern string, excludes []string) (matches, dirs []string, err error)
+	glob(pattern string) (matches []string, err error)
+	IsDir(name string) (bool, error)
+}
+
+// osFs implements FileSystem using the local disk.
+type osFs struct{}
+
+func (osFs) Open(name string) (io.ReadCloser, error) { return os.Open(name) }
+func (osFs) Exists(name string) (bool, bool, error) {
+	stat, err := os.Stat(name)
+	if err == nil {
+		return true, stat.IsDir(), nil
+	} else if os.IsNotExist(err) {
+		return false, false, nil
+	} else {
+		return false, false, err
+	}
+}
+
+func (osFs) IsDir(name string) (bool, error) {
+	info, err := os.Stat(name)
+	if err != nil {
+		return false, fmt.Errorf("unexpected error after glob: %s", err)
+	}
+	return info.IsDir(), nil
+}
+
+func (fs osFs) Glob(pattern string, excludes []string) (matches, dirs []string, err error) {
+	return startGlob(fs, pattern, excludes)
+}
+
+func (osFs) glob(pattern string) ([]string, error) {
+	return filepath.Glob(pattern)
+}
+
+type mockFs struct {
+	files map[string][]byte
+	dirs  map[string]bool
+	all   []string
+}
+
+func (m *mockFs) Open(name string) (io.ReadCloser, error) {
+	if f, ok := m.files[name]; ok {
+		return struct {
+			io.Closer
+			*bytes.Reader
+		}{
+			ioutil.NopCloser(nil),
+			bytes.NewReader(f),
+		}, nil
+	}
+
+	return nil, &os.PathError{
+		Op:   "open",
+		Path: name,
+		Err:  os.ErrNotExist,
+	}
+}
+
+func (m *mockFs) Exists(name string) (bool, bool, error) {
+	name = filepath.Clean(name)
+	if _, ok := m.files[name]; ok {
+		return ok, false, nil
+	}
+	if _, ok := m.dirs[name]; ok {
+		return ok, true, nil
+	}
+	return false, false, nil
+}
+
+func (m *mockFs) IsDir(name string) (bool, error) {
+	return m.dirs[filepath.Clean(name)], nil
+}
+
+func (m *mockFs) Glob(pattern string, excludes []string) (matches, dirs []string, err error) {
+	return startGlob(m, pattern, excludes)
+}
+
+func (m *mockFs) glob(pattern string) ([]string, error) {
+	var matches []string
+	for _, f := range m.all {
+		match, err := filepath.Match(pattern, f)
+		if err != nil {
+			return nil, err
+		}
+		if match {
+			matches = append(matches, f)
+		}
+	}
+	return matches, nil
+}
diff --git a/pathtools/glob.go b/pathtools/glob.go
index 286d933..a842a82 100644
--- a/pathtools/glob.go
+++ b/pathtools/glob.go
@@ -28,35 +28,25 @@
 var GlobMultipleRecursiveErr = errors.New("pattern contains multiple **")
 var GlobLastRecursiveErr = errors.New("pattern ** as last path element")
 
-// Glob returns the list of files that match the given pattern along with the
-// list of directories that were searched to construct the file list.
-// The supported glob patterns are equivalent to filepath.Glob, with an
-// extension that recursive glob (** matching zero or more complete path
-// entries) is supported. Glob also returns a list of directories that were
-// searched.
-//
-// In general ModuleContext.GlobWithDeps or SingletonContext.GlobWithDeps
-// should be used instead, as they will automatically set up dependencies
-// to rerun the primary builder when the list of matching files changes.
-func Glob(pattern string) (matches, dirs []string, err error) {
-	return GlobWithExcludes(pattern, nil)
-}
-
-// GlobWithExcludes returns the list of files that match the given pattern but
+// Glob returns the list of files that match the given pattern but
 // do not match the given exclude patterns, along with the list of directories
 // that were searched to construct the file list.  The supported glob and
 // exclude patterns are equivalent to filepath.Glob, with an extension that
 // recursive glob (** matching zero or more complete path entries) is supported.
-// GlobWithExcludes also returns a list of directories that were searched.
+// Glob also returns a list of directories that were searched.
 //
 // In general ModuleContext.GlobWithDeps or SingletonContext.GlobWithDeps
 // should be used instead, as they will automatically set up dependencies
 // to rerun the primary builder when the list of matching files changes.
-func GlobWithExcludes(pattern string, excludes []string) (matches, dirs []string, err error) {
+func Glob(pattern string, excludes []string) (matches, dirs []string, err error) {
+	return startGlob(OsFs, pattern, excludes)
+}
+
+func startGlob(fs FileSystem, pattern string, excludes []string) (matches, dirs []string, err error) {
 	if filepath.Base(pattern) == "**" {
 		return nil, nil, GlobLastRecursiveErr
 	} else {
-		matches, dirs, err = glob(pattern, false)
+		matches, dirs, err = glob(fs, pattern, false)
 	}
 
 	if err != nil {
@@ -73,12 +63,12 @@
 
 // glob is a recursive helper function to handle globbing each level of the pattern individually,
 // allowing searched directories to be tracked.  Also handles the recursive glob pattern, **.
-func glob(pattern string, hasRecursive bool) (matches, dirs []string, err error) {
+func glob(fs FileSystem, pattern string, hasRecursive bool) (matches, dirs []string, err error) {
 	if !isWild(pattern) {
 		// If there are no wilds in the pattern, check whether the file exists or not.
 		// Uses filepath.Glob instead of manually statting to get consistent results.
 		pattern = filepath.Clean(pattern)
-		matches, err = filepath.Glob(pattern)
+		matches, err = fs.glob(pattern)
 		if err != nil {
 			return matches, dirs, err
 		}
@@ -89,7 +79,7 @@
 			var matchDirs []string
 			for len(matchDirs) == 0 {
 				pattern, _ = saneSplit(pattern)
-				matchDirs, err = filepath.Glob(pattern)
+				matchDirs, err = fs.glob(pattern)
 				if err != nil {
 					return matches, dirs, err
 				}
@@ -108,17 +98,15 @@
 		hasRecursive = true
 	}
 
-	dirMatches, dirs, err := glob(dir, hasRecursive)
+	dirMatches, dirs, err := glob(fs, dir, hasRecursive)
 	if err != nil {
 		return nil, nil, err
 	}
 
 	for _, m := range dirMatches {
-		info, err := os.Stat(m)
-		if err != nil {
+		if isDir, err := fs.IsDir(m); err != nil {
 			return nil, nil, fmt.Errorf("unexpected error after glob: %s", err)
-		}
-		if info.IsDir() {
+		} else if isDir {
 			if file == "**" {
 				recurseDirs, err := walkAllDirs(m)
 				if err != nil {
@@ -127,7 +115,7 @@
 				matches = append(matches, recurseDirs...)
 			} else {
 				dirs = append(dirs, m)
-				newMatches, err := filepath.Glob(filepath.Join(m, file))
+				newMatches, err := fs.glob(filepath.Join(m, file))
 				if err != nil {
 					return nil, nil, err
 				}
@@ -316,7 +304,7 @@
 
 	for _, pattern := range patterns {
 		if isWild(pattern) {
-			matches, deps, err = Glob(filepath.Join(prefix, pattern))
+			matches, deps, err = Glob(filepath.Join(prefix, pattern), nil)
 			if err != nil {
 				return nil, nil, err
 			}
@@ -358,7 +346,7 @@
 // should be used instead, as they will automatically set up dependencies
 // to rerun the primary builder when the list of matching files changes.
 func GlobWithDepFile(glob, fileListFile, depFile string, excludes []string) (files []string, err error) {
-	files, dirs, err := GlobWithExcludes(glob, excludes)
+	files, dirs, err := Glob(glob, excludes)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pathtools/glob_test.go b/pathtools/glob_test.go
index 803cb6b..6e236fc 100644
--- a/pathtools/glob_test.go
+++ b/pathtools/glob_test.go
@@ -445,7 +445,7 @@
 	os.Chdir("testdata")
 	defer os.Chdir("..")
 	for _, testCase := range globTestCases {
-		matches, dirs, err := GlobWithExcludes(testCase.pattern, testCase.excludes)
+		matches, dirs, err := Glob(testCase.pattern, testCase.excludes)
 		if err != testCase.err {
 			t.Errorf(" pattern: %q", testCase.pattern)
 			if testCase.excludes != nil {
diff --git a/singleton_ctx.go b/singleton_ctx.go
index fc4a781..31195af 100644
--- a/singleton_ctx.go
+++ b/singleton_ctx.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"github.com/google/blueprint/pathtools"
 )
 
 type Singleton interface {
@@ -68,6 +69,8 @@
 	// builder whenever a file matching the pattern as added or removed, without rerunning if a
 	// file that does not match the pattern is added to a searched directory.
 	GlobWithDeps(pattern string, excludes []string) ([]string, error)
+
+	Fs() pathtools.FileSystem
 }
 
 var _ SingletonContext = (*singletonContext)(nil)
@@ -239,3 +242,7 @@
 	excludes []string) ([]string, error) {
 	return s.context.glob(pattern, excludes)
 }
+
+func (s *singletonContext) Fs() pathtools.FileSystem {
+	return s.context.fs
+}
diff --git a/tests/test_tree/build.ninja.in b/tests/test_tree/build.ninja.in
index 5604b40..7728ca3 100644
--- a/tests/test_tree/build.ninja.in
+++ b/tests/test_tree/build.ninja.in
@@ -58,7 +58,6 @@
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
         : g.bootstrap.compile ${g.bootstrap.srcDir}/blueprint/context.go $
-        ${g.bootstrap.srcDir}/blueprint/fs.go $
         ${g.bootstrap.srcDir}/blueprint/glob.go $
         ${g.bootstrap.srcDir}/blueprint/live_tracker.go $
         ${g.bootstrap.srcDir}/blueprint/mangle.go $
@@ -135,7 +134,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
-# Defined: blueprint/Blueprints:50:1
+# Defined: blueprint/Blueprints:49:1
 
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
@@ -151,7 +150,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
-# Defined: blueprint/Blueprints:34:1
+# Defined: blueprint/Blueprints:33:1
 
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
@@ -170,12 +169,13 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
-# Defined: blueprint/Blueprints:56:1
+# Defined: blueprint/Blueprints:55:1
 
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
         : g.bootstrap.compile $
         ${g.bootstrap.srcDir}/blueprint/pathtools/lists.go $
+        ${g.bootstrap.srcDir}/blueprint/pathtools/fs.go $
         ${g.bootstrap.srcDir}/blueprint/pathtools/glob.go | $
         ${g.bootstrap.compileCmd} $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a