From dacbf9b68db225a4721701cab5d2f7645a3b4d59 Mon Sep 17 00:00:00 2001 From: James Shubin Date: Thu, 8 Jun 2017 00:07:23 -0400 Subject: [PATCH] resources: Add resource sorting and clean tests Resource sorting is needed for comparing resource groups. --- resources/resources.go | 4 +- resources/resources_test.go | 80 ------------------- resources/util.go | 20 +++++ resources/util_test.go | 154 ++++++++++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+), 81 deletions(-) create mode 100644 resources/util_test.go diff --git a/resources/resources.go b/resources/resources.go index 665b0507..ce3d4cec 100644 --- a/resources/resources.go +++ b/resources/resources.go @@ -115,11 +115,13 @@ type ResData struct { // The Base interface is everything that is common to all resources. // Everything here only needs to be implemented once, in the BaseRes. type Base interface { + fmt.Stringer // String() string + GetName() string // can't be named "Name()" because of struct field SetName(string) SetKind(string) GetKind() string - String() string + Meta() *MetaParams Events() chan *event.Event Data() *ResData diff --git a/resources/resources_test.go b/resources/resources_test.go index 8c669cfc..3ae5fbd9 100644 --- a/resources/resources_test.go +++ b/resources/resources_test.go @@ -18,9 +18,6 @@ package resources import ( - "bytes" - "encoding/base64" - "encoding/gob" "testing" ) @@ -67,83 +64,6 @@ func TestCompare2(t *testing.T) { } } -func TestMiscEncodeDecode1(t *testing.T) { - var err error - //gob.Register( &NoopRes{} ) // happens in noop.go : init() - //gob.Register( &FileRes{} ) // happens in file.go : init() - // ... - - // encode - var input interface{} = &FileRes{} - b1 := bytes.Buffer{} - e := gob.NewEncoder(&b1) - err = e.Encode(&input) // pass with & - if err != nil { - t.Errorf("Gob failed to Encode: %v", err) - } - str := base64.StdEncoding.EncodeToString(b1.Bytes()) - - // decode - var output interface{} - bb, err := base64.StdEncoding.DecodeString(str) - if err != nil { - t.Errorf("Base64 failed to Decode: %v", err) - } - b2 := bytes.NewBuffer(bb) - d := gob.NewDecoder(b2) - err = d.Decode(&output) // pass with & - if err != nil { - t.Errorf("Gob failed to Decode: %v", err) - } - - res1, ok := input.(Res) - if !ok { - t.Errorf("Input %v is not a Res", res1) - return - } - res2, ok := output.(Res) - if !ok { - t.Errorf("Output %v is not a Res", res2) - return - } - if !res1.Compare(res2) { - t.Error("The input and output Res values do not match!") - } -} - -func TestMiscEncodeDecode2(t *testing.T) { - var err error - - // encode - input, _ := NewResource("file") - - b64, err := ResToB64(input) - if err != nil { - t.Errorf("Can't encode: %v", err) - return - } - - output, err := B64ToRes(b64) - if err != nil { - t.Errorf("Can't decode: %v", err) - return - } - - res1, ok := input.(Res) - if !ok { - t.Errorf("Input %v is not a Res", res1) - return - } - res2, ok := output.(Res) - if !ok { - t.Errorf("Output %v is not a Res", res2) - return - } - if !res1.Compare(res2) { - t.Error("The input and output Res values do not match!") - } -} - func TestIFF(t *testing.T) { uid := &BaseUID{Name: "/tmp/unit-test"} same := &BaseUID{Name: "/tmp/unit-test"} diff --git a/resources/util.go b/resources/util.go index e4fc053f..35c5035f 100644 --- a/resources/util.go +++ b/resources/util.go @@ -22,10 +22,30 @@ import ( "encoding/base64" "encoding/gob" "fmt" + "sort" errwrap "github.com/pkg/errors" ) +// ResourceSlice is a linear list of resources. It can be sorted. +type ResourceSlice []Res + +func (rs ResourceSlice) Len() int { return len(rs) } +func (rs ResourceSlice) Swap(i, j int) { rs[i], rs[j] = rs[j], rs[i] } +func (rs ResourceSlice) Less(i, j int) bool { return rs[i].String() < rs[j].String() } + +// Sort the list of resources and return a copy without modifying the input. +func Sort(rs []Res) []Res { + resources := []Res{} + for _, r := range rs { // copy + resources = append(resources, r) + } + sort.Sort(ResourceSlice(resources)) + return resources + // sort.Sort(ResourceSlice(rs)) // this is wrong, it would modify input! + //return rs +} + // ResToB64 encodes a resource to a base64 encoded string (after serialization). func ResToB64(res Res) (string, error) { b := bytes.Buffer{} diff --git a/resources/util_test.go b/resources/util_test.go new file mode 100644 index 00000000..8912f8b6 --- /dev/null +++ b/resources/util_test.go @@ -0,0 +1,154 @@ +// Mgmt +// Copyright (C) 2013-2017+ James Shubin and the project contributors +// Written by James Shubin and the project contributors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package resources + +import ( + "bytes" + "encoding/base64" + "encoding/gob" + "reflect" + "testing" +) + +func TestSort0(t *testing.T) { + rs := []Res{} + s := Sort(rs) + + if !reflect.DeepEqual(s, []Res{}) { + t.Errorf("sort failed!") + if s == nil { + t.Logf("output is nil!") + } else { + str := "Got:" + for _, r := range s { + str += " " + r.String() + } + t.Errorf(str) + } + } +} + +func TestSort1(t *testing.T) { + r1, _ := NewResource("noop") + r1.SetName("noop1") + r2, _ := NewResource("noop") + r2.SetName("noop2") + r3, _ := NewResource("noop") + r3.SetName("noop3") + r4, _ := NewResource("noop") + r4.SetName("noop4") + r5, _ := NewResource("noop") + r5.SetName("noop5") + r6, _ := NewResource("noop") + r6.SetName("noop6") + + rs := []Res{r3, r2, r6, r1, r5, r4} + s := Sort(rs) + + if !reflect.DeepEqual(s, []Res{r1, r2, r3, r4, r5, r6}) { + t.Errorf("sort failed!") + str := "Got:" + for _, r := range s { + str += " " + r.String() + } + t.Errorf(str) + } + + if !reflect.DeepEqual(rs, []Res{r3, r2, r6, r1, r5, r4}) { + t.Errorf("sort modified input!") + str := "Got:" + for _, r := range rs { + str += " " + r.String() + } + t.Errorf(str) + } +} + +func TestMiscEncodeDecode1(t *testing.T) { + var err error + + // encode + var input interface{} = &FileRes{} + b1 := bytes.Buffer{} + e := gob.NewEncoder(&b1) + err = e.Encode(&input) // pass with & + if err != nil { + t.Errorf("Gob failed to Encode: %v", err) + } + str := base64.StdEncoding.EncodeToString(b1.Bytes()) + + // decode + var output interface{} + bb, err := base64.StdEncoding.DecodeString(str) + if err != nil { + t.Errorf("Base64 failed to Decode: %v", err) + } + b2 := bytes.NewBuffer(bb) + d := gob.NewDecoder(b2) + err = d.Decode(&output) // pass with & + if err != nil { + t.Errorf("Gob failed to Decode: %v", err) + } + + res1, ok := input.(Res) + if !ok { + t.Errorf("Input %v is not a Res", res1) + return + } + res2, ok := output.(Res) + if !ok { + t.Errorf("Output %v is not a Res", res2) + return + } + if !res1.Compare(res2) { + t.Error("The input and output Res values do not match!") + } +} + +func TestMiscEncodeDecode2(t *testing.T) { + var err error + + // encode + input, _ := NewResource("file") + + b64, err := ResToB64(input) + if err != nil { + t.Errorf("Can't encode: %v", err) + return + } + + output, err := B64ToRes(b64) + if err != nil { + t.Errorf("Can't decode: %v", err) + return + } + + res1, ok := input.(Res) + if !ok { + t.Errorf("Input %v is not a Res", res1) + return + } + res2, ok := output.(Res) + if !ok { + t.Errorf("Output %v is not a Res", res2) + return + } + if !res1.Compare(res2) { + t.Error("The input and output Res values do not match!") + } +}