blob: 39e50f1f144ba523555b2f2bebe4508c5872e74f [file]
// Copyright 2018 The Bazel Authors. 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 shard
import (
"archive/zip"
"bytes"
"errors"
"fmt"
"reflect"
"sort"
"strings"
"testing"
)
func TestSepSharder(t *testing.T) {
tcs := []struct {
name string
sep string
wantName string
}{
{
name: "Hello",
sep: "/",
wantName: "Hello",
},
{
name: "foo/bar/baz",
sep: "/",
wantName: "foo/bar",
},
{
name: "com@google@Foo.dex",
sep: "@",
wantName: "com@google",
},
}
for _, tc := range tcs {
checkShard := func(name string, sc int) int {
if name != tc.wantName {
t.Errorf("makeSepSharder(%s).Shard(%s, 1): got name: %s wanted: %s", tc.sep, tc.name, name, tc.wantName)
}
return 0
}
s := MakeSepFunc(tc.sep, Func(checkShard))
s(tc.name, 1)
}
}
func TestBadSharder(t *testing.T) {
srcZip, err := makeZip(map[string]string{"hello.dex": "world"})
if err != nil {
t.Fatalf("Could not make initial zip: %v", err)
}
for _, shardVal := range []int{-1, -244, 123} {
zr, err := zip.NewReader(bytes.NewReader(srcZip), int64(len(srcZip)))
if err != nil {
t.Fatalf("could not read initial zip: %v", err)
}
zws := []*zip.Writer{zip.NewWriter(&bytes.Buffer{})}
s := Func(func(name string, sc int) int {
return shardVal
})
err = ZipShard(zr, zws, s)
if err == nil || !strings.Contains(err.Error(), "invalid shard index") {
t.Errorf("Returning shard value: %d gave: %v wanted an error with invalid shard index", shardVal, err)
}
}
}
func TestZipShard(t *testing.T) {
tcs := []struct {
name string
contents map[string]string
shardCount int
want map[int][]string
zipShardErr error
}{
{
name: "Vanilla",
contents: map[string]string{
"foo/hello.dex": "world",
"bar/something.dex": "stuff",
"blah/nothing.dex": "here",
"blah/everything.dex": "nowhere",
"hello/everything.dex": "nowhere",
},
shardCount: 5,
want: map[int][]string{
0: {"hello/everything.dex"},
3: {"foo/hello.dex", "bar/something.dex"},
4: {"blah/nothing.dex", "blah/everything.dex"},
},
},
{
name: "no output shards",
contents: map[string]string{"something.dex": "something"},
shardCount: 0,
zipShardErr: errors.New("no output writers"),
},
{
name: "empty input zip",
contents: map[string]string{},
shardCount: 5,
want: map[int][]string{},
},
}
for _, tc := range tcs {
srcZip, err := makeZip(tc.contents)
if err != nil {
t.Errorf("%s: could not create initial zip: %v", tc.name, err)
}
zr, err := zip.NewReader(bytes.NewReader(srcZip), int64(len(srcZip)))
if err != nil {
t.Errorf("%s: could not read initial zip: %v", tc.name, err)
continue
}
bufs := make([]*bytes.Buffer, tc.shardCount)
zws := make([]*zip.Writer, tc.shardCount)
for i := range zws {
bufs[i] = new(bytes.Buffer)
zws[i] = zip.NewWriter(bufs[i])
}
s := MakeSepFunc("/", Func(func(name string, sc int) int {
return len(name) % sc
}))
err = ZipShard(zr, zws, s)
if !reflect.DeepEqual(err, tc.zipShardErr) {
t.Errorf("%s: got zipshard error: %v wanted: %v", tc.name, err, tc.zipShardErr)
continue
}
for i, s := range bufs {
if err := zws[i].Close(); err != nil {
t.Errorf("%s: shard: %d cannot close zip writer: %v", tc.name, tc.shardCount, err)
continue
}
z, err := zip.NewReader(bytes.NewReader(s.Bytes()), int64(s.Len()))
if err != nil {
t.Errorf("%s: shard: %d cannot create zip reader: %v", tc.name, tc.shardCount, err)
continue
}
var fileNames []string
for _, f := range z.File {
fileNames = append(fileNames, f.Name)
}
sort.Strings(fileNames)
want, _ := tc.want[i]
sort.Strings(want)
if !reflect.DeepEqual(want, fileNames) {
t.Errorf("%s: shard: %d got: %s wanted: %s", tc.name, i, fileNames, want)
}
}
}
}
func makeZip(contents map[string]string) ([]byte, error) {
var zin bytes.Buffer
zw := zip.NewWriter(&zin)
for name, body := range contents {
f, err := zw.Create(name)
if err != nil {
return nil, fmt.Errorf("%s: could not create: %v", name, err)
}
_, err = f.Write([]byte(body))
if err != nil {
return nil, fmt.Errorf("%s: could not write: %s due to %v: ", name, body, err)
}
}
if err := zw.Close(); err != nil {
return nil, err
}
return zin.Bytes(), nil
}