blob: 91d88a5381a25d87a3b94c3a41f300d57efc0162 [file] [log] [blame]
// 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 main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"syscall"
"testing"
)
const attr = "user.gitsha1"
const checksum = "3f75526aa8f01eea5d76cee10722195dc73676de"
func createFSTree(names []string) (string, error) {
dir, err := ioutil.TempDir("", "")
if err != nil {
return dir, err
}
for _, f := range names {
p := filepath.Join(dir, f)
if err := os.MkdirAll(filepath.Dir(p), 0755); err != nil {
return dir, err
}
if err := ioutil.WriteFile(p, []byte{42}, 0644); err != nil {
return dir, err
}
if err := syscall.Setxattr(p, attr, []byte(checksum), 0); err != nil {
return dir, fmt.Errorf("Setxattr: %v", err)
}
}
return dir, nil
}
func TestConstruct(t *testing.T) {
dir, err := createFSTree([]string{
"toplevel",
"build/core/.git/HEAD",
"build/core/subdir/core.h",
"build/core/song/.git/HEAD",
"build/core/song/song.mp3",
"build/core/top",
"build/subfile",
})
if err != nil {
t.Fatal(err)
}
songT := &repoTree{
children: map[string]*repoTree{},
entries: map[string]*fileInfo{
"song.mp3": &fileInfo{isRegular: true, size: 1},
},
}
coreT := &repoTree{
children: map[string]*repoTree{"song": songT},
entries: map[string]*fileInfo{
"subdir/core.h": &fileInfo{isRegular: true, size: 1},
"top": &fileInfo{isRegular: true, size: 1},
},
}
topT := &repoTree{
children: map[string]*repoTree{
"build/core": coreT,
},
entries: map[string]*fileInfo{
"build/subfile": &fileInfo{isRegular: true, size: 1},
"toplevel": &fileInfo{
isRegular: true, size: 1},
},
}
got, err := newRepoTree(dir)
if err != nil {
t.Fatalf("newRepoTree: %v", err)
}
// Clear unpredictable data.
gotCh := got.allChildren()
for _, t := range gotCh {
for _, e := range t.entries {
e.inode = 0
}
}
wantCh := topT.allChildren()
for k, v := range wantCh {
if !reflect.DeepEqual(v, gotCh[k]) {
t.Fatalf("subrepo %q: got %#v want %#v", v, gotCh[k])
}
}
if !reflect.DeepEqual(got, topT) {
t.Errorf("got %#v want %#v", got, topT)
}
}
func TestPopulate(t *testing.T) {
dir, err := createFSTree([]string{
"ro/toplevel",
"ro/build/core/.gitid",
"ro/build/core/subdir/core.h",
"ro/build/core/song/.gitid",
"ro/build/core/song/song.mp3",
"ro/build/core/top",
"ro/build/subfile",
"ro/platform/art/.gitid",
"ro/platform/art/art.c",
"ro/platform/art/art.h",
"ro/platform/art/painting/.gitid",
"ro/platform/art/painting/picasso.c",
"rw/build/core/.git/head",
"rw/build/core/newdir/bla",
})
if err != nil {
t.Fatal("createFSTree:", err)
}
if err := os.Symlink(filepath.Join(dir, "ro/obsolete"), filepath.Join(dir, "rw/obsolete")); err != nil {
t.Errorf("Symlink: %v", err)
}
if err := populateCheckout(filepath.Join(dir, "ro"), filepath.Join(dir, "rw")); err != nil {
t.Errorf("populateCheckout: %v", err)
}
for _, f := range []string{
"build/core/newdir/bla",
"build/core/song/song.mp3",
"platform/art/art.c",
"platform/art/art.h",
"platform/art/painting/picasso.c",
} {
fn := filepath.Join(dir, "rw", f)
fi, err := os.Stat(fn)
if err != nil || fi.Size() != 1 {
t.Errorf("Stat(%s): %v, %v", fn, fi, err)
}
}
// The following files are in repo that has a r/w checkout, so
// they should not appear in the populated tree.
for _, f := range []string{
"ro/build/core/subdir/core.h",
"ro/build/core/top",
} {
fn := filepath.Join(dir, "rw", f)
_, err := os.Stat(fn)
if err == nil {
t.Errorf("file %s exists", fn)
}
}
if fi, err := os.Lstat(filepath.Join(dir, "rw/obsolete")); err == nil {
t.Fatalf("obsolete symlink still there: %v", fi)
}
}
func TestChangedFiles(t *testing.T) {
dir, err := createFSTree([]string{
"r1/manifest.xml",
"r1/same",
"r1/checksum",
"r1/size",
"r2/manifest.xml",
"r2/same",
"r2/checksum",
"r2/newfile",
"r2/size",
})
if err != nil {
t.Fatalf("createFSTree: %v", err)
}
if err := os.Symlink("same", filepath.Join(dir, "r2/symlink")); err != nil {
t.Fatalf("symlink: %v", err)
}
// same size, different checksum.
ck2 := "3f75526aa8f01eea5d76cee10722195dc73676df"
if err := syscall.Setxattr(filepath.Join(dir, "r2/checksum"), attr, []byte(ck2), 0); err != nil {
t.Fatalf("Setxattr: %v", err)
}
// different size.
if err := ioutil.WriteFile(filepath.Join(dir, "r2/size"), []byte("changed"), 0644); err != nil {
t.Fatalf("WriteFile(%s/r2/size): %v", dir, err)
}
// Manifest should be ignored.
if err := ioutil.WriteFile(filepath.Join(dir, "r2/manifest.xml"), []byte("changed"), 0644); err != nil {
t.Fatalf("WriteFile(%s/r2/manifest): %v", dir, err)
}
r2tree, err := newRepoTree(filepath.Join(dir, "r2"))
if err != nil {
t.Fatalf("newRepoTree: %v", err)
}
r1tree, err := newRepoTree(filepath.Join(dir, "r1"))
if err != nil {
t.Fatalf("newRepoTree: %v", err)
}
oldRoot := filepath.Join(dir, "r1")
got, err := changedFiles(oldRoot, r1tree.allFiles(), filepath.Join(dir, "r2"), r2tree.allFiles())
if err != nil {
t.Fatalf("changedFiles: %v", err)
}
if want := []string{"checksum", "newfile", "size"}; !reflect.DeepEqual(want, got) {
t.Errorf("got %v, want %v", got, want)
}
}
func TestClearEmptyDirs(t *testing.T) {
dir, err := createFSTree([]string{
"ro/build/sub/sub2/p1/.gitid",
"ro/build/sub/sub2/p1/build.mk",
"rw/build/proj/.git/HEAD",
"rw/build/proj/build.mk",
"r3/toplevel",
})
if err != nil {
t.Fatal("createFSTree:", err)
}
dest := filepath.Join(dir, "rw", "build/sub/sub2/p1")
if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil {
t.Errorf("MkdirAll: %v", err)
}
if err := os.Symlink(filepath.Join(dir, "ro", "build/sub/sub2/p1"), dest); err != nil {
t.Errorf("Symlink(%s): %v", dest, err)
}
if err := populateCheckout(filepath.Join(dir, "r3"), filepath.Join(dir, "rw")); err != nil {
t.Errorf("populateCheckout: %v", err)
}
gone := filepath.Join(dir, "rw", "build", "sub")
if fi, err := os.Lstat(gone); err == nil {
t.Errorf("directory %s still there: %v", gone, fi)
}
}