Merge pull request #76 from monnand/unit-test-manager

Unit test manager
This commit is contained in:
Victor Marmol 2014-07-07 16:56:39 -07:00
commit 46a9792ca8
4 changed files with 450 additions and 0 deletions

88
container/test/mock.go Normal file
View File

@ -0,0 +1,88 @@
// Copyright 2014 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 test
import (
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/info"
"github.com/stretchr/testify/mock"
)
// This struct mocks a container handler.
type MockContainerHandler struct {
mock.Mock
Name string
Aliases []string
}
// If self.Name is not empty, then ContainerReference() will return self.Name and self.Aliases.
// Otherwise, it will use the value provided by .On().Return().
func (self *MockContainerHandler) ContainerReference() (info.ContainerReference, error) {
if len(self.Name) > 0 {
var aliases []string
if len(self.Aliases) > 0 {
aliases = make([]string, len(self.Aliases))
copy(aliases, self.Aliases)
}
return info.ContainerReference{
Name: self.Name,
Aliases: aliases,
}, nil
}
args := self.Called()
return args.Get(0).(info.ContainerReference), args.Error(1)
}
func (self *MockContainerHandler) GetSpec() (*info.ContainerSpec, error) {
args := self.Called()
return args.Get(0).(*info.ContainerSpec), args.Error(1)
}
func (self *MockContainerHandler) GetStats() (*info.ContainerStats, error) {
args := self.Called()
return args.Get(0).(*info.ContainerStats), args.Error(1)
}
func (self *MockContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
args := self.Called(listType)
return args.Get(0).([]info.ContainerReference), args.Error(1)
}
func (self *MockContainerHandler) ListThreads(listType container.ListType) ([]int, error) {
args := self.Called(listType)
return args.Get(0).([]int), args.Error(1)
}
func (self *MockContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
args := self.Called(listType)
return args.Get(0).([]int), args.Error(1)
}
type FactoryForMockContainerHandler struct {
Name string
PrepareContainerHandlerFunc func(name string, handler *MockContainerHandler)
}
func (self *FactoryForMockContainerHandler) String() string {
return self.Name
}
func (self *FactoryForMockContainerHandler) NewContainerHandler(name string) (container.ContainerHandler, error) {
handler := &MockContainerHandler{}
if self.PrepareContainerHandlerFunc != nil {
self.PrepareContainerHandlerFunc(name, handler)
}
return handler, nil
}

68
info/test/datagen.go Normal file
View File

@ -0,0 +1,68 @@
// Copyright 2014 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 test
import (
"math"
"math/rand"
"time"
"github.com/google/cadvisor/info"
)
func GenerateRandomStats(numStats, numCores int, duration time.Duration) []*info.ContainerStats {
ret := make([]*info.ContainerStats, numStats)
perCoreUsages := make([]uint64, numCores)
currentTime := time.Now()
for i := range perCoreUsages {
perCoreUsages[i] = uint64(rand.Int63n(1000))
}
for i := 0; i < numStats; i++ {
stats := new(info.ContainerStats)
stats.Cpu = new(info.CpuStats)
stats.Memory = new(info.MemoryStats)
stats.Timestamp = currentTime
currentTime = currentTime.Add(duration)
percore := make([]uint64, numCores)
for i := range perCoreUsages {
perCoreUsages[i] += uint64(rand.Int63n(1000))
percore[i] = perCoreUsages[i]
stats.Cpu.Usage.Total += percore[i]
}
stats.Cpu.Usage.PerCpu = percore
stats.Cpu.Usage.User = stats.Cpu.Usage.Total
stats.Cpu.Usage.System = 0
stats.Memory.Usage = uint64(rand.Int63n(4096))
}
return ret
}
func GenerateRandomContainerSpec(numCores int) *info.ContainerSpec {
ret := &info.ContainerSpec{
Cpu: &info.CpuSpec{},
Memory: &info.MemorySpec{},
}
ret.Cpu.Limit = uint64(1000 + rand.Int63n(2000))
ret.Cpu.MaxLimit = uint64(1000 + rand.Int63n(2000))
n := (numCores + 63) / 64
ret.Cpu.Mask.Data = make([]uint64, n)
for i := 0; i < n; i++ {
ret.Cpu.Mask.Data[i] = math.MaxUint64
}
ret.Memory.Limit = uint64(4096 + rand.Int63n(4096))
return ret
}

237
manager/container_test.go Normal file
View File

@ -0,0 +1,237 @@
// Copyright 2014 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.
// Per-container manager.
package manager
import (
"fmt"
"reflect"
"testing"
"time"
"github.com/google/cadvisor/container"
ctest "github.com/google/cadvisor/container/test"
"github.com/google/cadvisor/info"
itest "github.com/google/cadvisor/info/test"
"github.com/google/cadvisor/storage"
stest "github.com/google/cadvisor/storage/test"
)
func createContainerDataAndSetHandler(
driver storage.StorageDriver,
f func(*ctest.MockContainerHandler),
t *testing.T,
) *containerData {
factory := &ctest.FactoryForMockContainerHandler{
Name: "factoryForMockContainer",
PrepareContainerHandlerFunc: func(name string, handler *ctest.MockContainerHandler) {
handler.Name = name
f(handler)
},
}
container.RegisterContainerHandlerFactory("/", factory)
if driver == nil {
driver = &stest.MockStorageDriver{}
}
ret, err := NewContainerData("/container", driver)
if err != nil {
t.Fatal(err)
}
return ret
}
func TestContainerUpdateSubcontainers(t *testing.T) {
var handler *ctest.MockContainerHandler
subcontainers := []info.ContainerReference{
{Name: "/container/ee0103"},
{Name: "/container/abcd"},
{Name: "/container/something"},
}
cd := createContainerDataAndSetHandler(
nil,
func(h *ctest.MockContainerHandler) {
h.On("ListContainers", container.LIST_SELF).Return(
subcontainers,
nil,
)
handler = h
},
t,
)
err := cd.updateSubcontainers()
if err != nil {
t.Fatal(err)
}
if len(cd.info.Subcontainers) != len(subcontainers) {
t.Errorf("Received %v subcontainers, should be %v", len(cd.info.Subcontainers), len(subcontainers))
}
for _, sub := range cd.info.Subcontainers {
found := false
for _, sub2 := range subcontainers {
if sub.Name == sub2.Name {
found = true
}
}
if !found {
t.Errorf("Received unknown sub container %v", sub)
}
}
handler.AssertExpectations(t)
}
func TestContainerUpdateSubcontainersWithError(t *testing.T) {
var handler *ctest.MockContainerHandler
cd := createContainerDataAndSetHandler(
nil,
func(h *ctest.MockContainerHandler) {
h.On("ListContainers", container.LIST_SELF).Return(
[]info.ContainerReference{},
fmt.Errorf("some error"),
)
handler = h
},
t,
)
err := cd.updateSubcontainers()
if err == nil {
t.Fatal("updateSubcontainers should return error")
}
if len(cd.info.Subcontainers) != 0 {
t.Errorf("Received %v subcontainers, should be 0", len(cd.info.Subcontainers))
}
handler.AssertExpectations(t)
}
func TestContainerUpdateStats(t *testing.T) {
var handler *ctest.MockContainerHandler
var ref info.ContainerReference
driver := &stest.MockStorageDriver{}
statsList := itest.GenerateRandomStats(1, 4, 1*time.Second)
stats := statsList[0]
cd := createContainerDataAndSetHandler(
driver,
func(h *ctest.MockContainerHandler) {
h.On("GetStats").Return(
stats,
nil,
)
handler = h
ref.Name = h.Name
},
t,
)
driver.On("AddStats", ref, stats).Return(nil)
err := cd.updateStats()
if err != nil {
t.Fatal(err)
}
handler.AssertExpectations(t)
}
func TestContainerUpdateSpec(t *testing.T) {
var handler *ctest.MockContainerHandler
spec := itest.GenerateRandomContainerSpec(4)
cd := createContainerDataAndSetHandler(
nil,
func(h *ctest.MockContainerHandler) {
h.On("GetSpec").Return(
spec,
nil,
)
handler = h
},
t,
)
err := cd.updateSpec()
if err != nil {
t.Fatal(err)
}
handler.AssertExpectations(t)
}
func TestContainerGetInfo(t *testing.T) {
var handler *ctest.MockContainerHandler
spec := itest.GenerateRandomContainerSpec(4)
subcontainers := []info.ContainerReference{
{Name: "/container/ee0103"},
{Name: "/container/abcd"},
{Name: "/container/something"},
}
aliases := []string{"a1", "a2"}
cd := createContainerDataAndSetHandler(
nil,
func(h *ctest.MockContainerHandler) {
h.On("GetSpec").Return(
spec,
nil,
)
h.On("ListContainers", container.LIST_SELF).Return(
subcontainers,
nil,
)
h.Aliases = aliases
handler = h
},
t,
)
info, err := cd.GetInfo()
if err != nil {
t.Fatal(err)
}
handler.AssertExpectations(t)
if len(info.Subcontainers) != len(subcontainers) {
t.Errorf("Received %v subcontainers, should be %v", len(info.Subcontainers), len(subcontainers))
}
for _, sub := range info.Subcontainers {
found := false
for _, sub2 := range subcontainers {
if sub.Name == sub2.Name {
found = true
}
}
if !found {
t.Errorf("Received unknown sub container %v", sub)
}
}
if !reflect.DeepEqual(spec, info.Spec) {
t.Errorf("received wrong container spec")
}
if info.Name != handler.Name {
t.Errorf("received wrong container name: received %v; should be %v", info.Name, handler.Name)
}
}

57
storage/test/mock.go Normal file
View File

@ -0,0 +1,57 @@
// Copyright 2014 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 test
import (
"github.com/google/cadvisor/info"
"github.com/stretchr/testify/mock"
)
type MockStorageDriver struct {
mock.Mock
MockCloseMethod bool
}
func (self *MockStorageDriver) AddStats(ref info.ContainerReference, stats *info.ContainerStats) error {
args := self.Called(ref, stats)
return args.Error(0)
}
func (self *MockStorageDriver) RecentStats(containerName string, numStats int) ([]*info.ContainerStats, error) {
args := self.Called(containerName, numStats)
return args.Get(0).([]*info.ContainerStats), args.Error(1)
}
func (self *MockStorageDriver) Percentiles(
containerName string,
cpuUsagePercentiles []int,
memUsagePercentiles []int,
) (*info.ContainerStatsPercentiles, error) {
args := self.Called(containerName, cpuUsagePercentiles, memUsagePercentiles)
return args.Get(0).(*info.ContainerStatsPercentiles), args.Error(1)
}
func (self *MockStorageDriver) Samples(containerName string, numSamples int) ([]*info.ContainerStatsSample, error) {
args := self.Called(containerName, numSamples)
return args.Get(0).([]*info.ContainerStatsSample), args.Error(1)
}
func (self *MockStorageDriver) Close() error {
if self.MockCloseMethod {
args := self.Called()
return args.Error(0)
}
return nil
}