pax_global_header00006660000000000000000000000064145611021360014511gustar00rootroot0000000000000052 comment=e8c2c6dee228df02bae64e27d1715fe62b682bb9 testing-1.2.0/000077500000000000000000000000001456110213600131665ustar00rootroot00000000000000testing-1.2.0/LICENCE000066400000000000000000000215011456110213600141520ustar00rootroot00000000000000All files in this repository are licensed as follows. If you contribute to this repository, it is assumed that you license your contribution under the same license unless you state otherwise. All files Copyright (C) 2015 Canonical Ltd. unless otherwise specified in the file. This software is licensed under the LGPLv3, included below. As a special exception to the GNU Lesser General Public License version 3 ("LGPL3"), the copyright holders of this Library give you permission to convey to a third party a Combined Work that links statically or dynamically to this Library without providing any Minimal Corresponding Source or Minimal Application Code as set out in 4d or providing the installation information set out in section 4e, provided that you comply with the other provisions of LGPL3 and provided that you meet, for the Application the terms and conditions of the license(s) which apply to the Application. Except as stated in this special exception, the provisions of LGPL3 will continue to comply in full to this Library. If you modify this Library, you may apply this exception to your version of this Library, but you are not obliged to do so. If you do not wish to do so, delete this exception statement from your version. This exception does not (and cannot) modify any license terms which apply to the Application, with which you must still comply. GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. testing-1.2.0/Makefile000066400000000000000000000000761456110213600146310ustar00rootroot00000000000000default: check check: go test ./... .PHONY: default check testing-1.2.0/README.md000066400000000000000000000001501456110213600144410ustar00rootroot00000000000000juju/testing ============ This package provides additional base test suites to be used with gocheck. testing-1.2.0/checkers/000077500000000000000000000000001456110213600147555ustar00rootroot00000000000000testing-1.2.0/checkers/LICENSE-golang000066400000000000000000000027071456110213600172350ustar00rootroot00000000000000Copyright (c) 2012 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. testing-1.2.0/checkers/bool.go000066400000000000000000000065171456110213600162500ustar00rootroot00000000000000// Copyright 2011 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers import ( "fmt" "reflect" gc "gopkg.in/check.v1" ) type isTrueChecker struct { *gc.CheckerInfo } // IsTrue checks whether a value has an underlying // boolean type and is true. var IsTrue gc.Checker = &isTrueChecker{ &gc.CheckerInfo{Name: "IsTrue", Params: []string{"obtained"}}, } // IsTrue checks whether a value has an underlying // boolean type and is false. var IsFalse gc.Checker = gc.Not(IsTrue) func (checker *isTrueChecker) Check(params []interface{}, names []string) (result bool, error string) { value := reflect.ValueOf(params[0]) if !value.IsValid() { return false, fmt.Sprintf("expected type bool, received %s", value) } switch value.Kind() { case reflect.Bool: return value.Bool(), "" } return false, fmt.Sprintf("expected type bool, received type %s", value.Type()) } type satisfiesChecker struct { *gc.CheckerInfo } // Satisfies checks whether a value causes the argument // function to return true. The function must be of // type func(T) bool where the value being checked // is assignable to T. var Satisfies gc.Checker = &satisfiesChecker{ &gc.CheckerInfo{ Name: "Satisfies", Params: []string{"obtained", "func(T) bool"}, }, } func (checker *satisfiesChecker) Check(params []interface{}, names []string) (result bool, error string) { f := reflect.ValueOf(params[1]) ft := f.Type() if ft.Kind() != reflect.Func || ft.NumIn() != 1 || ft.NumOut() != 1 || ft.Out(0) != reflect.TypeOf(true) { return false, fmt.Sprintf("expected func(T) bool, got %s", ft) } v := reflect.ValueOf(params[0]) if !v.IsValid() { if !canBeNil(ft.In(0)) { return false, fmt.Sprintf("cannot assign nil to argument %T", ft.In(0)) } v = reflect.Zero(ft.In(0)) } if !v.Type().AssignableTo(ft.In(0)) { return false, fmt.Sprintf("wrong argument type %s for %s", v.Type(), ft) } return f.Call([]reflect.Value{v})[0].Interface().(bool), "" } func canBeNil(t reflect.Type) bool { switch t.Kind() { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: return true } return false } type deepEqualsChecker struct { *gc.CheckerInfo } // The DeepEquals checker verifies that the obtained value is deep-equal to // the expected value. The check will work correctly even when facing // slices, interfaces, and values of different types (which always fail // the test). // // For example: // // c.Assert(value, DeepEquals, 42) // c.Assert(array, DeepEquals, []string{"hi", "there"}) // // This checker differs from gocheck.DeepEquals in that // it will compare a nil slice equal to an empty slice, // and a nil map equal to an empty map. var DeepEquals gc.Checker = &deepEqualsChecker{ &gc.CheckerInfo{Name: "DeepEquals", Params: []string{"obtained", "expected"}}, } func (checker *deepEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) { if ok, err := DeepEqual(params[0], params[1]); !ok { return false, err.Error() } return true, "" } type ignoreChecker struct { *gc.CheckerInfo } // Ignore always succeeds. var Ignore gc.Checker = &ignoreChecker{ &gc.CheckerInfo{Name: "Ignore", Params: []string{"obtained"}}, } func (checker *ignoreChecker) Check(params []interface{}, names []string) (result bool, error string) { return true, "" } testing-1.2.0/checkers/bool_test.go000066400000000000000000000053511456110213600173020ustar00rootroot00000000000000// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers_test import ( "errors" "os" gc "gopkg.in/check.v1" jc "github.com/juju/testing/checkers" ) type BoolSuite struct{} var _ = gc.Suite(&BoolSuite{}) func (s *BoolSuite) TestIsTrue(c *gc.C) { c.Assert(true, jc.IsTrue) c.Assert(false, gc.Not(jc.IsTrue)) result, msg := jc.IsTrue.Check([]interface{}{false}, nil) c.Assert(result, gc.Equals, false) c.Assert(msg, gc.Equals, "") result, msg = jc.IsTrue.Check([]interface{}{"foo"}, nil) c.Assert(result, gc.Equals, false) c.Check(msg, gc.Equals, `expected type bool, received type string`) result, msg = jc.IsTrue.Check([]interface{}{42}, nil) c.Assert(result, gc.Equals, false) c.Assert(msg, gc.Equals, `expected type bool, received type int`) result, msg = jc.IsTrue.Check([]interface{}{nil}, nil) c.Assert(result, gc.Equals, false) c.Assert(msg, gc.Matches, `expected type bool, received `) } func (s *BoolSuite) TestIsFalse(c *gc.C) { c.Check(false, jc.IsFalse) c.Check(true, gc.Not(jc.IsFalse)) } func is42(i int) bool { return i == 42 } var satisfiesTests = []struct { f interface{} arg interface{} result bool msg string }{{ f: is42, arg: 42, result: true, }, { f: is42, arg: 41, result: false, }, { f: is42, arg: "", result: false, msg: "wrong argument type string for func(int) bool", }, { f: os.IsNotExist, arg: errors.New("foo"), result: false, }, { f: os.IsNotExist, arg: os.ErrNotExist, result: true, }, { f: os.IsNotExist, arg: nil, result: false, }, { f: func(chan int) bool { return true }, arg: nil, result: true, }, { f: func(func()) bool { return true }, arg: nil, result: true, }, { f: func(interface{}) bool { return true }, arg: nil, result: true, }, { f: func(map[string]bool) bool { return true }, arg: nil, result: true, }, { f: func(*int) bool { return true }, arg: nil, result: true, }, { f: func([]string) bool { return true }, arg: nil, result: true, }} func (s *BoolSuite) TestSatisfies(c *gc.C) { for i, test := range satisfiesTests { c.Logf("test %d. %T %T", i, test.f, test.arg) result, msg := jc.Satisfies.Check([]interface{}{test.arg, test.f}, nil) c.Check(result, gc.Equals, test.result) c.Check(msg, gc.Equals, test.msg) } } func (s *BoolSuite) TestDeepEquals(c *gc.C) { for i, test := range deepEqualTests { c.Logf("test %d. %v == %v is %v", i, test.a, test.b, test.eq) result, msg := jc.DeepEquals.Check([]interface{}{test.a, test.b}, nil) c.Check(result, gc.Equals, test.eq) if test.eq { c.Check(msg, gc.Equals, "") } else { c.Check(msg, gc.Not(gc.Equals), "") } } } testing-1.2.0/checkers/checker.go000066400000000000000000000154551456110213600167220ustar00rootroot00000000000000// Copyright 2012, 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers import ( "fmt" "reflect" "strings" "time" gc "gopkg.in/check.v1" ) func TimeBetween(start, end time.Time) gc.Checker { if end.Before(start) { return &timeBetweenChecker{end, start} } return &timeBetweenChecker{start, end} } type timeBetweenChecker struct { start, end time.Time } func (checker *timeBetweenChecker) Info() *gc.CheckerInfo { info := gc.CheckerInfo{ Name: "TimeBetween", Params: []string{"obtained"}, } return &info } func (checker *timeBetweenChecker) Check(params []interface{}, names []string) (result bool, error string) { when, ok := params[0].(time.Time) if !ok { return false, "obtained value type must be time.Time" } if when.Before(checker.start) { return false, fmt.Sprintf("obtained time %q is before start time %q", when, checker.start) } if when.After(checker.end) { return false, fmt.Sprintf("obtained time %q is after end time %q", when, checker.end) } return true, "" } // DurationLessThan checker type durationLessThanChecker struct { *gc.CheckerInfo } var DurationLessThan gc.Checker = &durationLessThanChecker{ &gc.CheckerInfo{Name: "DurationLessThan", Params: []string{"obtained", "expected"}}, } func (checker *durationLessThanChecker) Check(params []interface{}, names []string) (result bool, error string) { obtained, ok := params[0].(time.Duration) if !ok { return false, "obtained value type must be time.Duration" } expected, ok := params[1].(time.Duration) if !ok { return false, "expected value type must be time.Duration" } return obtained.Nanoseconds() < expected.Nanoseconds(), "" } // HasPrefix checker for checking strings func stringOrStringer(value interface{}) (string, bool) { result, isString := value.(string) if !isString { if stringer, isStringer := value.(fmt.Stringer); isStringer { result, isString = stringer.String(), true } } return result, isString } type hasPrefixChecker struct { *gc.CheckerInfo } var HasPrefix gc.Checker = &hasPrefixChecker{ &gc.CheckerInfo{Name: "HasPrefix", Params: []string{"obtained", "expected"}}, } func (checker *hasPrefixChecker) Check(params []interface{}, names []string) (result bool, error string) { expected, ok := params[1].(string) if !ok { return false, "expected must be a string" } obtained, isString := stringOrStringer(params[0]) if isString { return strings.HasPrefix(obtained, expected), "" } return false, "Obtained value is not a string and has no .String()" } type hasSuffixChecker struct { *gc.CheckerInfo } var HasSuffix gc.Checker = &hasSuffixChecker{ &gc.CheckerInfo{Name: "HasSuffix", Params: []string{"obtained", "expected"}}, } func (checker *hasSuffixChecker) Check(params []interface{}, names []string) (result bool, error string) { expected, ok := params[1].(string) if !ok { return false, "expected must be a string" } obtained, isString := stringOrStringer(params[0]) if isString { return strings.HasSuffix(obtained, expected), "" } return false, "Obtained value is not a string and has no .String()" } type containsChecker struct { *gc.CheckerInfo } var Contains gc.Checker = &containsChecker{ &gc.CheckerInfo{Name: "Contains", Params: []string{"obtained", "expected"}}, } func (checker *containsChecker) Check(params []interface{}, names []string) (result bool, error string) { expected, ok := params[1].(string) if !ok { return false, "expected must be a string" } obtained, isString := stringOrStringer(params[0]) if isString { return strings.Contains(obtained, expected), "" } return false, "Obtained value is not a string and has no .String()" } type sameContents struct { *gc.CheckerInfo } // SameContents checks that the obtained slice contains all the values (and // same number of values) of the expected slice and vice versa, without respect // to order or duplicates. Uses DeepEquals on contents to compare. Content types // do not need to be hashable, but must satisfy reflect.DeepEquals. var SameContents gc.Checker = &sameContents{ &gc.CheckerInfo{Name: "SameContents", Params: []string{"obtained", "expected"}}, } func (checker *sameContents) Check(params []interface{}, names []string) (result bool, error string) { if len(params) != 2 { return false, "SameContents expects two slice arguments" } obtained := params[0] expected := params[1] tob := reflect.TypeOf(obtained) if tob.Kind() != reflect.Slice { return false, fmt.Sprintf("SameContents expects the obtained value to be a slice, got %q", tob.Kind()) } texp := reflect.TypeOf(expected) if texp.Kind() != reflect.Slice { return false, fmt.Sprintf("SameContents expects the expected value to be a slice, got %q", texp.Kind()) } if texp != tob { return false, fmt.Sprintf( "SameContents expects two slices of the same type, expected: %q, got: %q", texp, tob) } vexp := reflect.ValueOf(expected) vob := reflect.ValueOf(obtained) length := vexp.Len() if vob.Len() != length { // Slice has incorrect number of elements return false, "" } // left is the expected left := make([]any, 0, length) // right is the obtained right := make([]any, 0, length) for i := 0; i < length; i++ { left = append(left, reflect.Indirect(vexp.Index(i)).Interface()) right = append(right, reflect.Indirect(vob.Index(i)).Interface()) } outer: for i := 0; i < len(left); i++ { for j, r := range right { if reflect.DeepEqual(left[i], r) { left = append(left[:i], left[i+1:]...) right = append(right[:j], right[j+1:]...) i-- continue outer } } } return len(left) == 0 && len(right) == 0, "" } type errorIsNilChecker struct { *gc.CheckerInfo } // The ErrorIsNil checker tests whether the obtained value is nil. // Explicitly tests against only `nil`. // // For example: // // c.Assert(err, ErrorIsNil) var ErrorIsNil gc.Checker = &errorIsNilChecker{ &gc.CheckerInfo{Name: "ErrorIsNil", Params: []string{"value"}}, } type ErrorStacker interface { error StackTrace() []string } func (checker *errorIsNilChecker) Check(params []interface{}, names []string) (bool, string) { result, message := errorIsNil(params[0]) if !result { if stacker, ok := params[0].(ErrorStacker); ok && message == "" { stack := stacker.StackTrace() if stack != nil { message = "error stack:\n\t" + strings.Join(stack, "\n\t") } } } return result, message } func errorIsNil(obtained interface{}) (result bool, message string) { if obtained == nil { return true, "" } if _, ok := obtained.(error); !ok { return false, fmt.Sprintf("obtained type (%T) is not an error", obtained) } switch v := reflect.ValueOf(obtained); v.Kind() { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: if v.IsNil() { return false, fmt.Sprintf("value of (%T) is nil, but a typed nil", obtained) } } return false, "" } testing-1.2.0/checkers/checker_test.go000066400000000000000000000143751456110213600177610ustar00rootroot00000000000000// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers_test import ( "fmt" "testing" "time" gc "gopkg.in/check.v1" jc "github.com/juju/testing/checkers" ) func Test(t *testing.T) { gc.TestingT(t) } type CheckerSuite struct{} var _ = gc.Suite(&CheckerSuite{}) func (s *CheckerSuite) TestHasPrefix(c *gc.C) { c.Assert("foo bar", jc.HasPrefix, "foo") c.Assert("foo bar", gc.Not(jc.HasPrefix), "omg") } func (s *CheckerSuite) TestHasSuffix(c *gc.C) { c.Assert("foo bar", jc.HasSuffix, "bar") c.Assert("foo bar", gc.Not(jc.HasSuffix), "omg") } func (s *CheckerSuite) TestContains(c *gc.C) { c.Assert("foo bar baz", jc.Contains, "foo") c.Assert("foo bar baz", jc.Contains, "bar") c.Assert("foo bar baz", jc.Contains, "baz") c.Assert("foo bar baz", gc.Not(jc.Contains), "omg") } func (s *CheckerSuite) TestTimeBetween(c *gc.C) { now := time.Now() earlier := now.Add(-1 * time.Second) later := now.Add(time.Second) checkOK := func(value interface{}, start, end time.Time) { checker := jc.TimeBetween(start, end) value, msg := checker.Check([]interface{}{value}, nil) c.Check(value, jc.IsTrue) c.Check(msg, gc.Equals, "") } checkFails := func(value interface{}, start, end time.Time, match string) { checker := jc.TimeBetween(start, end) value, msg := checker.Check([]interface{}{value}, nil) c.Check(value, jc.IsFalse) c.Check(msg, gc.Matches, match) } checkOK(now, earlier, later) // Later can be before earlier... checkOK(now, later, earlier) // check at bounds checkOK(earlier, earlier, later) checkOK(later, earlier, later) checkFails(earlier, now, later, `obtained time .* is before start time .*`) checkFails(later, now, earlier, `obtained time .* is after end time .*`) checkFails(42, now, earlier, `obtained value type must be time.Time`) } type someStruct struct { a uint } func (s *CheckerSuite) TestSameContents(c *gc.C) { //// positive cases //// // same c.Check( []int{1, 2, 3}, jc.SameContents, []int{1, 2, 3}) // empty c.Check( []int{}, jc.SameContents, []int{}) // single c.Check( []int{1}, jc.SameContents, []int{1}) // different order c.Check( []int{1, 2, 3}, jc.SameContents, []int{3, 2, 1}) // multiple copies of same c.Check( []int{1, 1, 2}, jc.SameContents, []int{2, 1, 1}) type test struct { s string i int } // test structs c.Check( []test{{"a", 1}, {"b", 2}}, jc.SameContents, []test{{"b", 2}, {"a", 1}}) //// negative cases //// // different contents c.Check( []int{1, 3, 2, 5}, gc.Not(jc.SameContents), []int{5, 2, 3, 4}) // different size slices c.Check( []int{1, 2, 3}, gc.Not(jc.SameContents), []int{1, 2}) // different counts of same items c.Check( []int{1, 1, 2}, gc.Not(jc.SameContents), []int{1, 2, 2}) // Tests that check that we compare the contents of structs, // that we point to, not just the pointers to them. a1 := someStruct{1} a2 := someStruct{2} a3 := someStruct{3} b1 := someStruct{1} b2 := someStruct{2} // Same order, same contents c.Check( []*someStruct{&a1, &a2}, jc.SameContents, []*someStruct{&b1, &b2}) // Empty vs not c.Check( []*someStruct{&a1, &a2}, gc.Not(jc.SameContents), []*someStruct{}) // Empty vs empty // Same order, same contents c.Check( []*someStruct{}, jc.SameContents, []*someStruct{}) // Different order, same contents c.Check( []*someStruct{&a1, &a2}, jc.SameContents, []*someStruct{&b2, &b1}) // different contents c.Check( []*someStruct{&a3, &a2}, gc.Not(jc.SameContents), []*someStruct{&b2, &b1}) // Different sizes, same contents (duplicate item) c.Check( []*someStruct{&a1, &a2, &a1}, gc.Not(jc.SameContents), []*someStruct{&b2, &b1}) // Different sizes, same contents c.Check( []*someStruct{&a1, &a1, &a2}, gc.Not(jc.SameContents), []*someStruct{&b2, &b1}) // Same sizes, same contents, different quantities c.Check( []*someStruct{&a1, &a2, &a2}, gc.Not(jc.SameContents), []*someStruct{&b1, &b1, &b2}) /// Error cases /// // note: for these tests, we can't use gc.Not, since Not passes the error value through // and checks with a non-empty error always count as failed // Oddly, there doesn't seem to actually be a way to check for an error from a Checker. // different type res, err := jc.SameContents.Check([]interface{}{ []string{"1", "2"}, []int{1, 2}, }, []string{}) c.Check(res, jc.IsFalse) c.Check(err, gc.Not(gc.Equals), "") // obtained not a slice res, err = jc.SameContents.Check([]interface{}{ "test", []int{1}, }, []string{}) c.Check(res, jc.IsFalse) c.Check(err, gc.Not(gc.Equals), "") // expected not a slice res, err = jc.SameContents.Check([]interface{}{ []int{1}, "test", }, []string{}) c.Check(res, jc.IsFalse) c.Check(err, gc.Not(gc.Equals), "") } type stack_error struct { message string stack []string } type embedded struct { typed *stack_error err error } func (s *stack_error) Error() string { return s.message } func (s *stack_error) StackTrace() []string { return s.stack } type value_error string func (e value_error) Error() string { return string(e) } func (s *CheckerSuite) TestErrorIsNil(c *gc.C) { checkOK := func(value interface{}) { value, msg := jc.ErrorIsNil.Check([]interface{}{value}, nil) c.Check(value, jc.IsTrue) c.Check(msg, gc.Equals, "") } checkFails := func(value interface{}, match string) { value, msg := jc.ErrorIsNil.Check([]interface{}{value}, nil) c.Check(value, jc.IsFalse) c.Check(msg, gc.Matches, match) } var typedNil *stack_error var typedNilAsInterface error = typedNil var nilError error var value value_error var emptyValueErrorAsInterface error = value var embed embedded checkOK(nil) checkOK(nilError) checkOK(embed.err) checkFails([]string{}, `obtained type \(.*\) is not an error`) checkFails("", `obtained type \(.*\) is not an error`) checkFails(embed.typed, `value of \(.*\) is nil, but a typed nil`) checkFails(typedNilAsInterface, `value of \(.*\) is nil, but a typed nil`) checkFails(fmt.Errorf("an error"), "") checkFails(value, "") checkFails(emptyValueErrorAsInterface, "") emptyStack := &stack_error{"message", nil} checkFails(emptyStack, "") withStack := &stack_error{"message", []string{ "filename:line", "filename2:line2"}} checkFails(withStack, "error stack:\n\tfilename:line\n\tfilename2:line2") } testing-1.2.0/checkers/codec.go000066400000000000000000000044611456110213600163660ustar00rootroot00000000000000// Copyright 2012-2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers import ( "encoding/json" "fmt" gc "gopkg.in/check.v1" "gopkg.in/yaml.v2" ) type codecEqualChecker struct { name string marshal func(interface{}) ([]byte, error) unmarshal func([]byte, interface{}) error } // JSONEquals defines a checker that checks whether a byte slice, when // unmarshaled as JSON, is equal to the given value. // Rather than unmarshaling into something of the expected // body type, we reform the expected body in JSON and // back to interface{}, so we can check the whole content. // Otherwise we lose information when unmarshaling. var JSONEquals = &codecEqualChecker{ name: "JSONEquals", marshal: json.Marshal, unmarshal: json.Unmarshal, } // YAMLEquals defines a checker that checks whether a byte slice, when // unmarshaled as YAML, is equal to the given value. // Rather than unmarshaling into something of the expected // body type, we reform the expected body in YAML and // back to interface{}, so we can check the whole content. // Otherwise we lose information when unmarshaling. var YAMLEquals = &codecEqualChecker{ name: "YAMLEquals", marshal: yaml.Marshal, unmarshal: yaml.Unmarshal, } func (checker *codecEqualChecker) Info() *gc.CheckerInfo { return &gc.CheckerInfo{ Name: checker.name, Params: []string{"obtained", "expected"}, } } func (checker *codecEqualChecker) Check(params []interface{}, names []string) (result bool, error string) { gotContent, ok := params[0].(string) if !ok { return false, fmt.Sprintf("expected string, got %T", params[0]) } expectContent := params[1] expectContentBytes, err := checker.marshal(expectContent) if err != nil { return false, fmt.Sprintf("cannot marshal expected contents: %v", err) } var expectContentVal interface{} if err := checker.unmarshal(expectContentBytes, &expectContentVal); err != nil { return false, fmt.Sprintf("cannot unmarshal expected contents: %v", err) } var gotContentVal interface{} if err := checker.unmarshal([]byte(gotContent), &gotContentVal); err != nil { return false, fmt.Sprintf("cannot unmarshal obtained contents: %v; %q", err, gotContent) } if ok, err := DeepEqual(gotContentVal, expectContentVal); !ok { return false, err.Error() } return true, "" } testing-1.2.0/checkers/codec_test.go000066400000000000000000000074121456110213600174240ustar00rootroot00000000000000// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers_test import ( gc "gopkg.in/check.v1" jc "github.com/juju/testing/checkers" ) type Inner struct { First string Second int `json:",omitempty" yaml:",omitempty"` Third map[string]bool `json:",omitempty" yaml:",omitempty"` } type Outer struct { First float64 Second []*Inner `json:"Last,omitempty" yaml:"last,omitempty"` } func (s *CheckerSuite) TestJSONEquals(c *gc.C) { tests := []struct { descr string obtained string expected *Outer result bool msg string }{ { descr: "very simple", obtained: `{"First": 47.11}`, expected: &Outer{ First: 47.11, }, result: true, }, { descr: "nested", obtained: `{"First": 47.11, "Last": [{"First": "Hello", "Second": 42}]}`, expected: &Outer{ First: 47.11, Second: []*Inner{ {First: "Hello", Second: 42}, }, }, result: true, }, { descr: "nested with newline", obtained: `{"First": 47.11, "Last": [{"First": "Hello", "Second": 42}, {"First": "World", "Third": {"T": true, "F": false}}]}`, expected: &Outer{ First: 47.11, Second: []*Inner{ {First: "Hello", Second: 42}, {First: "World", Third: map[string]bool{ "F": false, "T": true, }}, }, }, result: true, }, { descr: "illegal field", obtained: `{"NotThere": 47.11}`, expected: &Outer{ First: 47.11, }, result: false, msg: `mismatch at .*: validity mismatch; .*`, }, { descr: "illegal optained content", obtained: `{"NotThere": `, result: false, msg: `cannot unmarshal obtained contents: unexpected end of JSON input; .*`, }, } for i, test := range tests { c.Logf("test #%d) %s", i, test.descr) result, msg := jc.JSONEquals.Check([]interface{}{test.obtained, test.expected}, nil) c.Check(result, gc.Equals, test.result) c.Check(msg, gc.Matches, test.msg) } // Test non-string input. result, msg := jc.JSONEquals.Check([]interface{}{true, true}, nil) c.Check(result, gc.Equals, false) c.Check(msg, gc.Matches, "expected string, got bool") } func (s *CheckerSuite) TestYAMLEquals(c *gc.C) { tests := []struct { descr string obtained string expected *Outer result bool msg string }{ { descr: "very simple", obtained: `first: 47.11`, expected: &Outer{ First: 47.11, }, result: true, }, { descr: "nested", obtained: `{first: 47.11, last: [{first: 'Hello', second: 42}]}`, expected: &Outer{ First: 47.11, Second: []*Inner{ {First: "Hello", Second: 42}, }, }, result: true, }, { descr: "nested with newline", obtained: `{first: 47.11, last: [{first: 'Hello', second: 42}, {first: 'World', third: {t: true, f: false}}]}`, expected: &Outer{ First: 47.11, Second: []*Inner{ {First: "Hello", Second: 42}, {First: "World", Third: map[string]bool{ "f": false, "t": true, }}, }, }, result: true, }, { descr: "illegal field", obtained: `{"NotThere": 47.11}`, expected: &Outer{ First: 47.11, }, result: false, msg: `mismatch at .*: validity mismatch; .*`, }, { descr: "illegal obtained content", obtained: `{"NotThere": `, result: false, msg: `cannot unmarshal obtained contents: yaml: line 1: .*`, }, } for i, test := range tests { c.Logf("test #%d) %s", i, test.descr) result, msg := jc.YAMLEquals.Check([]interface{}{test.obtained, test.expected}, nil) c.Check(result, gc.Equals, test.result) c.Check(msg, gc.Matches, test.msg) } // Test non-string input. result, msg := jc.YAMLEquals.Check([]interface{}{true, true}, nil) c.Check(result, gc.Equals, false) c.Check(msg, gc.Matches, "expected string, got bool") } testing-1.2.0/checkers/deepequal.go000066400000000000000000000250371456110213600172600ustar00rootroot00000000000000// Copied with small adaptations from the reflect package in the // Go source tree. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE-golang file. package checkers import ( "fmt" "reflect" "strings" "time" "unsafe" ) var timeType = reflect.TypeOf(time.Time{}) // During deepValueEqual, must keep track of checks that are // in progress. The comparison algorithm assumes that all // checks in progress are true when it reencounters them. // Visited comparisons are stored in a map indexed by visit. type visit struct { a1 uintptr a2 uintptr typ reflect.Type } type mismatchError struct { v1, v2 reflect.Value path string how string } func (err *mismatchError) Error() string { path := err.path if path == "" { path = "top level" } return fmt.Sprintf("mismatch at %s: %s; obtained %#v; expected %#v", path, err.how, printable(err.v1), printable(err.v2)) } func printable(v reflect.Value) interface{} { vi := interfaceOf(v) switch vi := vi.(type) { case time.Time: return vi.UTC().Format(time.RFC3339Nano) default: return vi } } // Tests for deep equality using reflected types. The map argument tracks // comparisons that have already been seen, which allows short circuiting on // recursive types. func deepValueEqual(path string, v1, v2 reflect.Value, visited map[visit]bool, depth int, customCheckFunc CustomCheckFunc) (ok bool, err error) { errorf := func(f string, a ...interface{}) error { return &mismatchError{ v1: v1, v2: v2, path: strings.Replace(path, topLevel, "", 1), how: fmt.Sprintf(f, a...), } } if !v1.IsValid() || !v2.IsValid() { if v1.IsValid() == v2.IsValid() { return true, nil } return false, errorf("validity mismatch") } if v1.Type() != v2.Type() { return false, errorf("type mismatch %s vs %s", v1.Type(), v2.Type()) } // if depth > 10 { panic("deepValueEqual") } // for debugging hard := func(k reflect.Kind) bool { switch k { case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct: return true } return false } if v1.CanAddr() && v2.CanAddr() && hard(v1.Kind()) { addr1 := v1.UnsafeAddr() addr2 := v2.UnsafeAddr() if addr1 > addr2 { // Canonicalize order to reduce number of entries in visited. addr1, addr2 = addr2, addr1 } // Short circuit if references are identical ... if addr1 == addr2 { return true, nil } // ... or already seen typ := v1.Type() v := visit{addr1, addr2, typ} if visited[v] { return true, nil } // Remember for later. visited[v] = true } if customCheckFunc != nil { useDefault, equal, err := customCheckFunc(path, interfaceOf(v1), interfaceOf(v2)) if !useDefault { return equal, err } } switch v1.Kind() { case reflect.Array: if v1.Len() != v2.Len() { // can't happen! return false, errorf("length mismatch, %d vs %d", v1.Len(), v2.Len()) } for i := 0; i < v1.Len(); i++ { if ok, err := deepValueEqual( fmt.Sprintf("%s[%d]", path, i), v1.Index(i), v2.Index(i), visited, depth+1, customCheckFunc); !ok { return false, err } } return true, nil case reflect.Slice: // We treat a nil slice the same as an empty slice. if v1.Len() != v2.Len() { return false, errorf("length mismatch, %d vs %d", v1.Len(), v2.Len()) } if v1.Pointer() == v2.Pointer() { return true, nil } for i := 0; i < v1.Len(); i++ { if ok, err := deepValueEqual( fmt.Sprintf("%s[%d]", path, i), v1.Index(i), v2.Index(i), visited, depth+1, customCheckFunc); !ok { return false, err } } return true, nil case reflect.Interface: if v1.IsNil() || v2.IsNil() { if v1.IsNil() != v2.IsNil() { return false, errorf("nil vs non-nil interface mismatch") } return true, nil } return deepValueEqual(path, v1.Elem(), v2.Elem(), visited, depth+1, customCheckFunc) case reflect.Ptr: return deepValueEqual("(*"+path+")", v1.Elem(), v2.Elem(), visited, depth+1, customCheckFunc) case reflect.Struct: if v1.Type() == timeType { // Special case for time - we ignore the time zone. t1 := interfaceOf(v1).(time.Time) t2 := interfaceOf(v2).(time.Time) if t1.Equal(t2) { return true, nil } return false, errorf("unequal") } for i, n := 0, v1.NumField(); i < n; i++ { path := path + "." + v1.Type().Field(i).Name if ok, err := deepValueEqual(path, v1.Field(i), v2.Field(i), visited, depth+1, customCheckFunc); !ok { return false, err } } return true, nil case reflect.Map: if v1.IsNil() != v2.IsNil() { return false, errorf("nil vs non-nil mismatch") } if v1.Len() != v2.Len() { return false, errorf("length mismatch, %d vs %d", v1.Len(), v2.Len()) } if v1.Pointer() == v2.Pointer() { return true, nil } for _, k := range v1.MapKeys() { var p string if k.CanInterface() { p = path + "[" + fmt.Sprintf("%#v", k.Interface()) + "]" } else { p = path + "[someKey]" } if ok, err := deepValueEqual(p, v1.MapIndex(k), v2.MapIndex(k), visited, depth+1, customCheckFunc); !ok { return false, err } } return true, nil case reflect.Func: if v1.IsNil() && v2.IsNil() { return true, nil } // Can't do better than this: return false, errorf("non-nil functions") case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if v1.Int() != v2.Int() { return false, errorf("unequal") } return true, nil case reflect.Uint, reflect.Uintptr, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if v1.Uint() != v2.Uint() { return false, errorf("unequal") } return true, nil case reflect.Float32, reflect.Float64: if v1.Float() != v2.Float() { return false, errorf("unequal") } return true, nil case reflect.Complex64, reflect.Complex128: if v1.Complex() != v2.Complex() { return false, errorf("unequal") } return true, nil case reflect.Bool: if v1.Bool() != v2.Bool() { return false, errorf("unequal") } return true, nil case reflect.String: if v1.String() != v2.String() { return false, errorf("unequal") } return true, nil case reflect.Chan, reflect.UnsafePointer: if v1.Pointer() != v2.Pointer() { return false, errorf("unequal") } return true, nil default: panic("unexpected type " + v1.Type().String()) } } // DeepEqual tests for deep equality. It uses normal == equality where // possible but will scan elements of arrays, slices, maps, and fields // of structs. In maps, keys are compared with == but elements use deep // equality. DeepEqual correctly handles recursive types. Functions are // equal only if they are both nil. // // DeepEqual differs from reflect.DeepEqual in two ways: // - an empty slice is considered equal to a nil slice. // - two time.Time values that represent the same instant // but with different time zones are considered equal. // // If the two values compare unequal, the resulting error holds the // first difference encountered. func DeepEqual(a1, a2 interface{}) (bool, error) { errorf := func(f string, a ...interface{}) error { return &mismatchError{ v1: reflect.ValueOf(a1), v2: reflect.ValueOf(a2), path: "", how: fmt.Sprintf(f, a...), } } if a1 == nil || a2 == nil { if a1 == a2 { return true, nil } return false, errorf("nil vs non-nil mismatch") } v1 := reflect.ValueOf(a1) v2 := reflect.ValueOf(a2) if v1.Type() != v2.Type() { return false, errorf("type mismatch %s vs %s", v1.Type(), v2.Type()) } return deepValueEqual(topLevel, v1, v2, make(map[visit]bool), 0, nil) } // DeepEqualWithCustomCheck tests for deep equality. It uses normal == equality where // possible but will scan elements of arrays, slices, maps, and fields // of structs. In maps, keys are compared with == but elements use deep // equality. DeepEqual correctly handles recursive types. Functions are // equal only if they are both nil. // // DeepEqual differs from reflect.DeepEqual in two ways: // - an empty slice is considered equal to a nil slice. // - two time.Time values that represent the same instant // but with different time zones are considered equal. // // If the two values compare unequal, the resulting error holds the // first difference encountered. // // If both values are interface-able and customCheckFunc is non nil, // customCheckFunc will be invoked. If it returns useDefault as true, the // DeepEqual continues, otherwise the result of the customCheckFunc is used. func DeepEqualWithCustomCheck(a1 interface{}, a2 interface{}, customCheckFunc CustomCheckFunc) (bool, error) { errorf := func(f string, a ...interface{}) error { return &mismatchError{ v1: reflect.ValueOf(a1), v2: reflect.ValueOf(a2), path: "", how: fmt.Sprintf(f, a...), } } if a1 == nil || a2 == nil { if a1 == a2 { return true, nil } return false, errorf("nil vs non-nil mismatch") } v1 := reflect.ValueOf(a1) v2 := reflect.ValueOf(a2) if v1.Type() != v2.Type() { return false, errorf("type mismatch %s vs %s", v1.Type(), v2.Type()) } return deepValueEqual(topLevel, v1, v2, make(map[visit]bool), 0, customCheckFunc) } // CustomCheckFunc should return true for useDefault if DeepEqualWithCustomCheck should behave like DeepEqual. // Otherwise the result of the CustomCheckFunc is used. type CustomCheckFunc func(path string, a1 interface{}, a2 interface{}) (useDefault bool, equal bool, err error) // interfaceOf returns v.Interface() even if v.CanInterface() == false. // This enables us to call fmt.Printf on a value even if it's derived // from inside an unexported field. // See https://code.google.com/p/go/issues/detail?id=8965 // for a possible future alternative to this hack. func interfaceOf(v reflect.Value) interface{} { if !v.IsValid() { return nil } return bypassCanInterface(v).Interface() } type flag uintptr var flagValOffset = func() uintptr { field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") if !ok { panic("reflect.Value has no flag field") } return field.Offset }() func flagField(v *reflect.Value) *flag { return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) } // bypassCanInterface returns a version of v that // bypasses the CanInterface check. func bypassCanInterface(v reflect.Value) reflect.Value { if !v.IsValid() || v.CanInterface() { return v } *flagField(&v) &^= flagRO return v } // Sanity checks against future reflect package changes // to the type or semantics of the Value.flag field. func init() { field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") if !ok { panic("reflect.Value has no flag field") } if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { panic("reflect.Value flag field has changed kind") } } testing-1.2.0/checkers/deepequal_test.go000066400000000000000000000166001456110213600203130ustar00rootroot00000000000000// Copied with small adaptations from the reflect package in the // Go source tree. We use testing rather than gocheck to preserve // as much source equivalence as possible. // TODO tests for error messages // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE-golang file. package checkers_test import ( "regexp" "testing" "time" "github.com/juju/testing/checkers" ) func deepEqual(a1, a2 interface{}) bool { ok, _ := checkers.DeepEqual(a1, a2) return ok } type Basic struct { x int y float32 } type NotBasic Basic type DeepEqualTest struct { a, b interface{} eq bool msg string } // Simple functions for DeepEqual tests. var ( fn1 func() // nil. fn2 func() // nil. fn3 = func() { fn1() } // Not nil. ) var deepEqualTests = []DeepEqualTest{ // Equalities {nil, nil, true, ""}, {1, 1, true, ""}, {int32(1), int32(1), true, ""}, {0.5, 0.5, true, ""}, {float32(0.5), float32(0.5), true, ""}, {"hello", "hello", true, ""}, {make([]int, 10), make([]int, 10), true, ""}, {&[3]int{1, 2, 3}, &[3]int{1, 2, 3}, true, ""}, {Basic{1, 0.5}, Basic{1, 0.5}, true, ""}, {error(nil), error(nil), true, ""}, {map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true, ""}, {fn1, fn2, true, ""}, {time.Unix(0, 0), time.Unix(0, 0), true, ""}, // Same time from different zones (difference from normal DeepEqual) {time.Unix(0, 0).UTC(), time.Unix(0, 0).In(time.FixedZone("FOO", 60*60)), true, ""}, // Inequalities {1, 2, false, `mismatch at top level: unequal; obtained 1; expected 2`}, {int32(1), int32(2), false, `mismatch at top level: unequal; obtained 1; expected 2`}, {0.5, 0.6, false, `mismatch at top level: unequal; obtained 0\.5; expected 0\.6`}, {float32(0.5), float32(0.6), false, `mismatch at top level: unequal; obtained 0\.5; expected 0\.6`}, {"hello", "hey", false, `mismatch at top level: unequal; obtained "hello"; expected "hey"`}, {make([]int, 10), make([]int, 11), false, `mismatch at top level: length mismatch, 10 vs 11; obtained \[\]int\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\}; expected \[\]int\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\}`}, {&[3]int{1, 2, 3}, &[3]int{1, 2, 4}, false, `mismatch at \(\*\)\[2\]: unequal; obtained 3; expected 4`}, {Basic{1, 0.5}, Basic{1, 0.6}, false, `mismatch at \.y: unequal; obtained 0\.5; expected 0\.6`}, {Basic{1, 0}, Basic{2, 0}, false, `mismatch at \.x: unequal; obtained 1; expected 2`}, {map[int]string{1: "one", 3: "two"}, map[int]string{2: "two", 1: "one"}, false, `mismatch at \[3\]: validity mismatch; obtained "two"; expected `}, {map[int]string{1: "one", 2: "txo"}, map[int]string{2: "two", 1: "one"}, false, `mismatch at \[2\]: unequal; obtained "txo"; expected "two"`}, {map[int]string{1: "one"}, map[int]string{2: "two", 1: "one"}, false, `mismatch at top level: length mismatch, 1 vs 2; obtained map\[int\]string\{1:"one"\}; expected map\[int\]string\{.*\}`}, {map[int]string{2: "two", 1: "one"}, map[int]string{1: "one"}, false, `mismatch at top level: length mismatch, 2 vs 1; obtained map\[int\]string\{.*\}; expected map\[int\]string\{1:"one"\}`}, {nil, 1, false, `mismatch at top level: nil vs non-nil mismatch; obtained ; expected 1`}, {1, nil, false, `mismatch at top level: nil vs non-nil mismatch; obtained 1; expected `}, {fn1, fn3, false, `mismatch at top level: non-nil functions; obtained \(func\(\)\)\(nil\); expected \(func\(\)\)\(0x[0-9a-f]+\)`}, {fn3, fn3, false, `mismatch at top level: non-nil functions; obtained \(func\(\)\)\(0x[0-9a-f]+\); expected \(func\(\)\)\(0x[0-9a-f]+\)`}, {[]interface{}{nil}, []interface{}{"a"}, false, `mismatch at \[0\]: nil vs non-nil interface mismatch`}, // Nil vs empty: they're the same (difference from normal DeepEqual) {[]int{}, []int(nil), true, ""}, {[]int{}, []int{}, true, ""}, {[]int(nil), []int(nil), true, ""}, // Mismatched types {1, 1.0, false, `mismatch at top level: type mismatch int vs float64; obtained 1; expected 1`}, {int32(1), int64(1), false, `mismatch at top level: type mismatch int32 vs int64; obtained 1; expected 1`}, {0.5, "hello", false, `mismatch at top level: type mismatch float64 vs string; obtained 0\.5; expected "hello"`}, {[]int{1, 2, 3}, [3]int{1, 2, 3}, false, `mismatch at top level: type mismatch \[\]int vs \[3\]int; obtained \[\]int\{1, 2, 3\}; expected \[3\]int\{1, 2, 3\}`}, {&[3]interface{}{1, 2, 4}, &[3]interface{}{1, 2, "s"}, false, `mismatch at \(\*\)\[2\]: type mismatch int vs string; obtained 4; expected "s"`}, {Basic{1, 0.5}, NotBasic{1, 0.5}, false, `mismatch at top level: type mismatch checkers_test\.Basic vs checkers_test\.NotBasic; obtained checkers_test\.Basic\{x:1, y:0\.5\}; expected checkers_test\.NotBasic\{x:1, y:0\.5\}`}, {time.Unix(0, 0).UTC(), time.Unix(0, 0).In(time.FixedZone("FOO", 60*60)).Add(1), false, `mismatch at top level: unequal; obtained "1970-01-01T00:00:00Z"; expected "1970-01-01T00:00:00.000000001Z"`}, {time.Unix(0, 0).UTC(), time.Unix(0, 0).Add(1), false, `mismatch at top level: unequal; obtained "1970-01-01T00:00:00Z"; expected "1970-01-01T00:00:00.000000001Z"`}, { map[uint]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, false, `mismatch at top level: type mismatch map\[uint\]string vs map\[int\]string; obtained map\[uint\]string\{.*\}; expected map\[int\]string\{.*\}`, }, } func TestDeepEqual(t *testing.T) { for _, test := range deepEqualTests { r, err := checkers.DeepEqual(test.a, test.b) if r != test.eq { t.Errorf("deepEqual(%v, %v) = %v, want %v", test.a, test.b, r, test.eq) } if test.eq { if err != nil { t.Errorf("deepEqual(%v, %v): unexpected error message %q when equal", test.a, test.b, err) } continue } if err == nil { t.Errorf("deepEqual(%v, %v); mismatch but nil error", test.a, test.b) continue } if ok, _ := regexp.MatchString(test.msg, err.Error()); !ok { t.Errorf("deepEqual(%v, %v); unexpected error %q, want %q", test.a, test.b, err.Error(), test.msg) } } } type Recursive struct { x int r *Recursive } func TestDeepEqualRecursiveStruct(t *testing.T) { a, b := new(Recursive), new(Recursive) *a = Recursive{12, a} *b = Recursive{12, b} if !deepEqual(a, b) { t.Error("deepEqual(recursive same) = false, want true") } } type _Complex struct { a int b [3]*_Complex c *string d map[float64]float64 } func TestDeepEqualComplexStruct(t *testing.T) { m := make(map[float64]float64) stra, strb := "hello", "hello" a, b := new(_Complex), new(_Complex) *a = _Complex{5, [3]*_Complex{a, b, a}, &stra, m} *b = _Complex{5, [3]*_Complex{b, a, a}, &strb, m} if !deepEqual(a, b) { t.Error("deepEqual(complex same) = false, want true") } } func TestDeepEqualComplexStructInequality(t *testing.T) { m := make(map[float64]float64) stra, strb := "hello", "helloo" // Difference is here a, b := new(_Complex), new(_Complex) *a = _Complex{5, [3]*_Complex{a, b, a}, &stra, m} *b = _Complex{5, [3]*_Complex{b, a, a}, &strb, m} if deepEqual(a, b) { t.Error("deepEqual(complex different) = true, want false") } } type UnexpT struct { m map[int]int } func TestDeepEqualUnexportedMap(t *testing.T) { // Check that DeepEqual can look at unexported fields. x1 := UnexpT{map[int]int{1: 2}} x2 := UnexpT{map[int]int{1: 2}} if !deepEqual(&x1, &x2) { t.Error("deepEqual(x1, x2) = false, want true") } y1 := UnexpT{map[int]int{2: 3}} if deepEqual(&x1, &y1) { t.Error("deepEqual(x1, y1) = true, want false") } } testing-1.2.0/checkers/error.go000066400000000000000000000022051456110213600164340ustar00rootroot00000000000000// Copyright 2023 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers import ( "errors" "fmt" "reflect" gc "gopkg.in/check.v1" ) type errorIsChecker struct { *gc.CheckerInfo } // ErrorIs checks whether a value is an error that matches the other // argument. var ErrorIs gc.Checker = &errorIsChecker{ CheckerInfo: &gc.CheckerInfo{ Name: "ErrorIs", Params: []string{"obtained", "error"}, }, } var ( errType = reflect.TypeOf((*error)(nil)).Elem() ) func (checker *errorIsChecker) Check(params []interface{}, names []string) (result bool, err string) { if params[1] == nil || params[0] == nil { return params[1] == params[0], "" } f := reflect.ValueOf(params[1]) ft := f.Type() if !ft.Implements(errType) { return false, fmt.Sprintf("wrong error target type, got: %s", ft) } v := reflect.ValueOf(params[0]) vt := v.Type() if !v.IsValid() { return false, fmt.Sprintf("wrong argument type %s for %s", vt, ft) } if !vt.Implements(errType) { return false, fmt.Sprintf("wrong argument type %s for %s", vt, ft) } return errors.Is(v.Interface().(error), f.Interface().(error)), "" } testing-1.2.0/checkers/error_test.go000066400000000000000000000026771456110213600175100ustar00rootroot00000000000000package checkers_test import ( "fmt" "github.com/juju/errors" jc "github.com/juju/testing/checkers" gc "gopkg.in/check.v1" ) type ErrorSuite struct{} var _ = gc.Suite(&ErrorSuite{}) var errorIsTests = []struct { arg interface{} target interface{} result bool msg string }{{ arg: fmt.Errorf("bar"), target: nil, result: false, }, { arg: nil, target: fmt.Errorf("bar"), result: false, }, { arg: nil, target: nil, result: true, }, { arg: fmt.Errorf("bar"), target: fmt.Errorf("foo"), result: false, }, { arg: errors.ConstError("bar"), target: errors.ConstError("foo"), result: false, }, { arg: errors.ConstError("foo"), target: errors.ConstError("foo"), result: true, }, { arg: errors.Trace(errors.ConstError("foo")), target: errors.ConstError("foo"), result: true, }, { arg: errors.ConstError("foo"), target: "blah", msg: "wrong error target type, got: string", }, { arg: "blah", target: errors.ConstError("foo"), msg: "wrong argument type string for errors.ConstError", }, { arg: (*error)(nil), target: errors.ConstError("foo"), msg: "wrong argument type *error for errors.ConstError", }} func (s *ErrorSuite) TestErrorIs(c *gc.C) { for i, test := range errorIsTests { c.Logf("test %d. %T %T", i, test.arg, test.target) result, msg := jc.ErrorIs.Check([]interface{}{test.arg, test.target}, nil) c.Check(result, gc.Equals, test.result) c.Check(msg, gc.Equals, test.msg) } } testing-1.2.0/checkers/file.go000066400000000000000000000142661456110213600162340ustar00rootroot00000000000000// Copyright 2013 Canonical Ltd. // Copyright 2014 Cloudbase Solutions SRL // Licensed under the LGPLv3, see LICENCE file for details. package checkers import ( "fmt" "os" "path/filepath" "reflect" "runtime" "strings" gc "gopkg.in/check.v1" ) // IsNonEmptyFile checker type isNonEmptyFileChecker struct { *gc.CheckerInfo } var IsNonEmptyFile gc.Checker = &isNonEmptyFileChecker{ &gc.CheckerInfo{Name: "IsNonEmptyFile", Params: []string{"obtained"}}, } func (checker *isNonEmptyFileChecker) Check(params []interface{}, names []string) (result bool, error string) { filename, isString := stringOrStringer(params[0]) if isString { fileInfo, err := os.Stat(filename) if os.IsNotExist(err) { return false, fmt.Sprintf("%s does not exist", filename) } else if err != nil { return false, fmt.Sprintf("other stat error: %v", err) } if fileInfo.Size() > 0 { return true, "" } else { return false, fmt.Sprintf("%s is empty", filename) } } value := reflect.ValueOf(params[0]) return false, fmt.Sprintf("obtained value is not a string and has no .String(), %s:%#v", value.Kind(), params[0]) } // IsDirectory checker type isDirectoryChecker struct { *gc.CheckerInfo } var IsDirectory gc.Checker = &isDirectoryChecker{ &gc.CheckerInfo{Name: "IsDirectory", Params: []string{"obtained"}}, } func (checker *isDirectoryChecker) Check(params []interface{}, names []string) (result bool, error string) { path, isString := stringOrStringer(params[0]) if isString { fileInfo, err := os.Stat(path) if os.IsNotExist(err) { return false, fmt.Sprintf("%s does not exist", path) } else if err != nil { return false, fmt.Sprintf("other stat error: %v", err) } if fileInfo.IsDir() { return true, "" } else { return false, fmt.Sprintf("%s is not a directory", path) } } value := reflect.ValueOf(params[0]) return false, fmt.Sprintf("obtained value is not a string and has no .String(), %s:%#v", value.Kind(), params[0]) } // IsSymlink checker type isSymlinkChecker struct { *gc.CheckerInfo } var IsSymlink gc.Checker = &isSymlinkChecker{ &gc.CheckerInfo{Name: "IsSymlink", Params: []string{"obtained"}}, } func (checker *isSymlinkChecker) Check(params []interface{}, names []string) (result bool, error string) { path, isString := stringOrStringer(params[0]) if isString { fileInfo, err := os.Lstat(path) if os.IsNotExist(err) { return false, fmt.Sprintf("%s does not exist", path) } else if err != nil { return false, fmt.Sprintf("other stat error: %v", err) } if fileInfo.Mode()&os.ModeSymlink != 0 { return true, "" } else { return false, fmt.Sprintf("%s is not a symlink: %+v", path, fileInfo) } } value := reflect.ValueOf(params[0]) return false, fmt.Sprintf("obtained value is not a string and has no .String(), %s:%#v", value.Kind(), params[0]) } // DoesNotExist checker makes sure the path specified doesn't exist. type doesNotExistChecker struct { *gc.CheckerInfo } var DoesNotExist gc.Checker = &doesNotExistChecker{ &gc.CheckerInfo{Name: "DoesNotExist", Params: []string{"obtained"}}, } func (checker *doesNotExistChecker) Check(params []interface{}, names []string) (result bool, error string) { path, isString := stringOrStringer(params[0]) if isString { _, err := os.Stat(path) if os.IsNotExist(err) { return true, "" } else if err != nil { return false, fmt.Sprintf("other stat error: %v", err) } return false, fmt.Sprintf("%s exists", path) } value := reflect.ValueOf(params[0]) return false, fmt.Sprintf("obtained value is not a string and has no .String(), %s:%#v", value.Kind(), params[0]) } // SymlinkDoesNotExist checker makes sure the path specified doesn't exist. type symlinkDoesNotExistChecker struct { *gc.CheckerInfo } var SymlinkDoesNotExist gc.Checker = &symlinkDoesNotExistChecker{ &gc.CheckerInfo{Name: "SymlinkDoesNotExist", Params: []string{"obtained"}}, } func (checker *symlinkDoesNotExistChecker) Check(params []interface{}, names []string) (result bool, error string) { path, isString := stringOrStringer(params[0]) if isString { _, err := os.Lstat(path) if os.IsNotExist(err) { return true, "" } else if err != nil { return false, fmt.Sprintf("other stat error: %v", err) } return false, fmt.Sprintf("%s exists", path) } value := reflect.ValueOf(params[0]) return false, fmt.Sprintf("obtained value is not a string and has no .String(), %s:%#v", value.Kind(), params[0]) } // Same path checker -- will check that paths are the same OS indepentent type samePathChecker struct { *gc.CheckerInfo } // SamePath checks paths to see whether they're the same, can follow symlinks and is OS independent var SamePath gc.Checker = &samePathChecker{ &gc.CheckerInfo{Name: "SamePath", Params: []string{"obtained", "expected"}}, } func (checker *samePathChecker) Check(params []interface{}, names []string) (result bool, error string) { // Check for panics defer func() { if panicked := recover(); panicked != nil { result = false error = fmt.Sprint(panicked) } }() // Convert input obtained, isStr := stringOrStringer(params[0]) if !isStr { return false, fmt.Sprintf("obtained value is not a string and has no .String(), %T:%#v", params[0], params[0]) } expected, isStr := stringOrStringer(params[1]) if !isStr { return false, fmt.Sprintf("obtained value is not a string and has no .String(), %T:%#v", params[1], params[1]) } // Convert paths to proper format obtained = filepath.FromSlash(obtained) expected = filepath.FromSlash(expected) // If running on Windows, paths will be case-insensitive and thus we // normalize the inputs to a default of all upper-case if runtime.GOOS == "windows" { obtained = strings.ToUpper(obtained) expected = strings.ToUpper(expected) } // Same path do not check further if obtained == expected { return true, "" } // If it's not the same path, check if it points to the same file. // Thus, the cases with windows-shortened paths are accounted for // This will throw an error if it's not a file ob, err := os.Stat(obtained) if err != nil { return false, err.Error() } ex, err := os.Stat(expected) if err != nil { return false, err.Error() } res := os.SameFile(ob, ex) if res { return true, "" } return false, fmt.Sprintf("Not the same file") } testing-1.2.0/checkers/file_test.go000066400000000000000000000211261456110213600172640ustar00rootroot00000000000000// Copyright 2013 Canonical Ltd. // Copyright 2014 Cloudbase Solutions SRL // Licensed under the LGPLv3, see LICENCE file for details. package checkers_test import ( "fmt" "io/ioutil" "os" "path/filepath" "runtime" "strings" gc "gopkg.in/check.v1" jc "github.com/juju/testing/checkers" ) type FileSuite struct{} var _ = gc.Suite(&FileSuite{}) func (s *FileSuite) TestIsNonEmptyFile(c *gc.C) { file, err := ioutil.TempFile(c.MkDir(), "") c.Assert(err, gc.IsNil) fmt.Fprintf(file, "something") file.Close() c.Assert(file.Name(), jc.IsNonEmptyFile) } func (s *FileSuite) TestIsNonEmptyFileWithEmptyFile(c *gc.C) { file, err := ioutil.TempFile(c.MkDir(), "") c.Assert(err, gc.IsNil) file.Close() result, message := jc.IsNonEmptyFile.Check([]interface{}{file.Name()}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, file.Name()+" is empty") } func (s *FileSuite) TestIsNonEmptyFileWithMissingFile(c *gc.C) { name := filepath.Join(c.MkDir(), "missing") result, message := jc.IsNonEmptyFile.Check([]interface{}{name}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, name+" does not exist") } func (s *FileSuite) TestIsNonEmptyFileWithNumber(c *gc.C) { result, message := jc.IsNonEmptyFile.Check([]interface{}{42}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, "obtained value is not a string and has no .String(), int:42") } func (s *FileSuite) TestIsDirectory(c *gc.C) { dir := c.MkDir() c.Assert(dir, jc.IsDirectory) } func (s *FileSuite) TestIsDirectoryMissing(c *gc.C) { absentDir := filepath.Join(c.MkDir(), "foo") result, message := jc.IsDirectory.Check([]interface{}{absentDir}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, absentDir+" does not exist") } func (s *FileSuite) TestIsDirectoryWithFile(c *gc.C) { file, err := ioutil.TempFile(c.MkDir(), "") c.Assert(err, gc.IsNil) file.Close() result, message := jc.IsDirectory.Check([]interface{}{file.Name()}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, file.Name()+" is not a directory") } func (s *FileSuite) TestIsDirectoryWithNumber(c *gc.C) { result, message := jc.IsDirectory.Check([]interface{}{42}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, "obtained value is not a string and has no .String(), int:42") } func (s *FileSuite) TestDoesNotExist(c *gc.C) { absentDir := filepath.Join(c.MkDir(), "foo") c.Assert(absentDir, jc.DoesNotExist) } func (s *FileSuite) TestDoesNotExistWithPath(c *gc.C) { dir := c.MkDir() result, message := jc.DoesNotExist.Check([]interface{}{dir}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, dir+" exists") } func (s *FileSuite) TestDoesNotExistWithSymlink(c *gc.C) { dir := c.MkDir() deadPath := filepath.Join(dir, "dead") symlinkPath := filepath.Join(dir, "a-symlink") err := os.Symlink(deadPath, symlinkPath) c.Assert(err, gc.IsNil) // A valid symlink pointing to something that doesn't exist passes. // Use SymlinkDoesNotExist to check for the non-existence of the link itself. c.Assert(symlinkPath, jc.DoesNotExist) } func (s *FileSuite) TestDoesNotExistWithNumber(c *gc.C) { result, message := jc.DoesNotExist.Check([]interface{}{42}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, "obtained value is not a string and has no .String(), int:42") } func (s *FileSuite) TestSymlinkDoesNotExist(c *gc.C) { absentDir := filepath.Join(c.MkDir(), "foo") c.Assert(absentDir, jc.SymlinkDoesNotExist) } func (s *FileSuite) TestSymlinkDoesNotExistWithPath(c *gc.C) { dir := c.MkDir() result, message := jc.SymlinkDoesNotExist.Check([]interface{}{dir}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, dir+" exists") } func (s *FileSuite) TestSymlinkDoesNotExistWithSymlink(c *gc.C) { dir := c.MkDir() deadPath := filepath.Join(dir, "dead") symlinkPath := filepath.Join(dir, "a-symlink") err := os.Symlink(deadPath, symlinkPath) c.Assert(err, gc.IsNil) result, message := jc.SymlinkDoesNotExist.Check([]interface{}{symlinkPath}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, symlinkPath+" exists") } func (s *FileSuite) TestSymlinkDoesNotExistWithNumber(c *gc.C) { result, message := jc.SymlinkDoesNotExist.Check([]interface{}{42}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, "obtained value is not a string and has no .String(), int:42") } func (s *FileSuite) TestIsSymlink(c *gc.C) { file, err := ioutil.TempFile(c.MkDir(), "") c.Assert(err, gc.IsNil) c.Log(file.Name()) c.Log(filepath.Dir(file.Name())) symlinkPath := filepath.Join(filepath.Dir(file.Name()), "a-symlink") err = os.Symlink(file.Name(), symlinkPath) c.Assert(err, gc.IsNil) c.Assert(symlinkPath, jc.IsSymlink) } func (s *FileSuite) TestIsSymlinkWithFile(c *gc.C) { file, err := ioutil.TempFile(c.MkDir(), "") c.Assert(err, gc.IsNil) result, message := jc.IsSymlink.Check([]interface{}{file.Name()}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, jc.Contains, " is not a symlink") } func (s *FileSuite) TestIsSymlinkWithDir(c *gc.C) { result, message := jc.IsSymlink.Check([]interface{}{c.MkDir()}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, jc.Contains, " is not a symlink") } func (s *FileSuite) TestSamePathWithNumber(c *gc.C) { result, message := jc.SamePath.Check([]interface{}{42, 52}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, "obtained value is not a string and has no .String(), int:42") } func (s *FileSuite) TestSamePathBasic(c *gc.C) { dir := c.MkDir() result, message := jc.SamePath.Check([]interface{}{dir, dir}, nil) c.Assert(result, jc.IsTrue) c.Assert(message, gc.Equals, "") } type SamePathLinuxSuite struct{} var _ = gc.Suite(&SamePathLinuxSuite{}) func (s *SamePathLinuxSuite) SetUpSuite(c *gc.C) { if runtime.GOOS == "windows" { c.Skip("Skipped Linux-intented SamePath tests on Windows.") } } func (s *SamePathLinuxSuite) TestNotSamePathLinuxBasic(c *gc.C) { dir := c.MkDir() path1 := filepath.Join(dir, "Test") path2 := filepath.Join(dir, "test") result, message := jc.SamePath.Check([]interface{}{path1, path2}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, "stat "+path1+": no such file or directory") } func (s *SamePathLinuxSuite) TestSamePathLinuxSymlinks(c *gc.C) { file, err := ioutil.TempFile(c.MkDir(), "") c.Assert(err, gc.IsNil) symlinkPath := filepath.Join(filepath.Dir(file.Name()), "a-symlink") err = os.Symlink(file.Name(), symlinkPath) result, message := jc.SamePath.Check([]interface{}{file.Name(), symlinkPath}, nil) c.Assert(result, jc.IsTrue) c.Assert(message, gc.Equals, "") } type SamePathWindowsSuite struct{} var _ = gc.Suite(&SamePathWindowsSuite{}) func (s *SamePathWindowsSuite) SetUpSuite(c *gc.C) { if runtime.GOOS != "windows" { c.Skip("Skipped Windows-intented SamePath tests.") } } func (s *SamePathWindowsSuite) TestNotSamePathBasic(c *gc.C) { dir := c.MkDir() path1 := filepath.Join(dir, "notTest") path2 := filepath.Join(dir, "test") result, message := jc.SamePath.Check([]interface{}{path1, path2}, nil) c.Assert(result, jc.IsFalse) path1 = strings.ToUpper(path1) c.Assert(message, gc.Equals, "GetFileAttributesEx "+path1+": The system cannot find the file specified.") } func (s *SamePathWindowsSuite) TestSamePathWindowsCaseInsensitive(c *gc.C) { dir := c.MkDir() path1 := filepath.Join(dir, "Test") path2 := filepath.Join(dir, "test") result, message := jc.SamePath.Check([]interface{}{path1, path2}, nil) c.Assert(result, jc.IsTrue) c.Assert(message, gc.Equals, "") } func (s *SamePathWindowsSuite) TestSamePathWindowsFixSlashes(c *gc.C) { result, message := jc.SamePath.Check([]interface{}{"C:/Users", "C:\\Users"}, nil) c.Assert(result, jc.IsTrue) c.Assert(message, gc.Equals, "") } func (s *SamePathWindowsSuite) TestSamePathShortenedPaths(c *gc.C) { dir := c.MkDir() dir1, err := ioutil.TempDir(dir, "Programming") defer os.Remove(dir1) c.Assert(err, gc.IsNil) result, message := jc.SamePath.Check([]interface{}{dir + "\\PROGRA~1", dir1}, nil) c.Assert(result, jc.IsTrue) c.Assert(message, gc.Equals, "") } func (s *SamePathWindowsSuite) TestSamePathShortenedPathsConsistent(c *gc.C) { dir := c.MkDir() dir1, err := ioutil.TempDir(dir, "Programming") defer os.Remove(dir1) c.Assert(err, gc.IsNil) dir2, err := ioutil.TempDir(dir, "Program Files") defer os.Remove(dir2) c.Assert(err, gc.IsNil) result, message := jc.SamePath.Check([]interface{}{dir + "\\PROGRA~1", dir2}, nil) c.Assert(result, gc.Not(jc.IsTrue)) c.Assert(message, gc.Equals, "Not the same file") result, message = jc.SamePath.Check([]interface{}{"C:/PROGRA~2", "C:/Program Files (x86)"}, nil) c.Assert(result, jc.IsTrue) c.Assert(message, gc.Equals, "") } testing-1.2.0/checkers/flags_1_12.go000066400000000000000000000003011456110213600171140ustar00rootroot00000000000000// Copyright 2020 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. // +build go1.12 // +build !go1.13,!go1.14 package checkers const ( flagRO flag = 1<<5 | 1<<6 ) testing-1.2.0/checkers/flags_1_13.go000066400000000000000000000003011456110213600171150ustar00rootroot00000000000000// Copyright 2020 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. // +build go1.13 // +build !go1.12,!go1.14 package checkers const ( flagRO flag = 1<<5 | 1<<6 ) testing-1.2.0/checkers/flags_1_14.go000066400000000000000000000002471456110213600171270ustar00rootroot00000000000000// Copyright 2020 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. // +build go1.14 package checkers const ( flagRO flag = 1<<5 | 1<<6 ) testing-1.2.0/checkers/log.go000066400000000000000000000054411456110213600160710ustar00rootroot00000000000000// Copyright 2012, 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers import ( "fmt" "regexp" "strings" "github.com/juju/loggo/v2" gc "gopkg.in/check.v1" ) type SimpleMessage struct { Level loggo.Level Message string } type SimpleMessages []SimpleMessage func (s SimpleMessage) String() string { return fmt.Sprintf("%s %s", s.Level, s.Message) } func (s SimpleMessages) GoString() string { out := make([]string, len(s)) for i, m := range s { out[i] = m.String() } return fmt.Sprintf("SimpleMessages{\n%s\n}", strings.Join(out, "\n")) } func logToSimpleMessages(log []loggo.Entry) SimpleMessages { out := make(SimpleMessages, len(log)) for i, val := range log { out[i].Level = val.Level out[i].Message = val.Message } return out } type logMatches struct { *gc.CheckerInfo } func (checker *logMatches) Check(params []interface{}, _ []string) (result bool, error string) { var obtained SimpleMessages switch params[0].(type) { case []loggo.Entry: obtained = logToSimpleMessages(params[0].([]loggo.Entry)) default: return false, "Obtained value must be of type []loggo.Entry or SimpleMessage" } var expected SimpleMessages switch param := params[1].(type) { case []SimpleMessage: expected = SimpleMessages(param) case SimpleMessages: expected = param case []string: expected = make(SimpleMessages, len(param)) for i, s := range param { expected[i] = SimpleMessage{ Message: s, Level: loggo.UNSPECIFIED, } } default: return false, "Expected value must be of type []string or []SimpleMessage" } obtainedSinceLastMatch := obtained for len(expected) > 0 && len(obtained) >= len(expected) { var msg SimpleMessage msg, obtained = obtained[0], obtained[1:] expect := expected[0] if expect.Level != loggo.UNSPECIFIED && msg.Level != expect.Level { continue } if matched, err := regexp.MatchString(expect.Message, msg.Message); err != nil { return false, fmt.Sprintf("bad message regexp %q: %v", expect.Message, err) } else if !matched { continue } expected = expected[1:] obtainedSinceLastMatch = obtained } if len(obtained) < len(expected) { params[0] = obtainedSinceLastMatch params[1] = expected return false, "" } return true, "" } // LogMatches checks whether a given TestLogValues actually contains the log // messages we expected. If you compare it against a list of strings, we only // compare that the strings in the messages are correct. You can alternatively // pass a slice of SimpleMessage and we will check that the log levels are // also correct. // // The log may contain additional messages before and after each of the specified // expected messages. var LogMatches gc.Checker = &logMatches{ &gc.CheckerInfo{Name: "LogMatches", Params: []string{"obtained", "expected"}}, } testing-1.2.0/checkers/log_test.go000066400000000000000000000135671456110213600171400ustar00rootroot00000000000000// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers_test import ( "github.com/juju/loggo/v2" gc "gopkg.in/check.v1" jc "github.com/juju/testing/checkers" ) type SimpleMessageSuite struct{} var _ = gc.Suite(&SimpleMessageSuite{}) func (s *SimpleMessageSuite) TestSimpleMessageString(c *gc.C) { m := jc.SimpleMessage{ Level: loggo.INFO, Message: `hello world `, } c.Check(m.String(), gc.Matches, "INFO hello\nworld\n") } func (s *SimpleMessageSuite) TestSimpleMessagesGoString(c *gc.C) { m := jc.SimpleMessages{{ Level: loggo.DEBUG, Message: "debug", }, { Level: loggo.ERROR, Message: "Error", }} c.Check(m.GoString(), gc.Matches, `SimpleMessages{ DEBUG debug ERROR Error }`) } type LogMatchesSuite struct{} var _ = gc.Suite(&LogMatchesSuite{}) func (s *LogMatchesSuite) TestMatchSimpleMessage(c *gc.C) { log := []loggo.Entry{ {Level: loggo.INFO, Message: "foo bar"}, {Level: loggo.INFO, Message: "12345"}, } c.Check(log, jc.LogMatches, []jc.SimpleMessage{ {loggo.INFO, "foo bar"}, {loggo.INFO, "12345"}, }) c.Check(log, jc.LogMatches, []jc.SimpleMessage{ {loggo.INFO, "foo .*"}, {loggo.INFO, "12345"}, }) // UNSPECIFIED means we don't care what the level is, // just check the message string matches. c.Check(log, jc.LogMatches, []jc.SimpleMessage{ {loggo.UNSPECIFIED, "foo .*"}, {loggo.INFO, "12345"}, }) c.Check(log, gc.Not(jc.LogMatches), []jc.SimpleMessage{ {loggo.INFO, "foo bar"}, {loggo.DEBUG, "12345"}, }) } func (s *LogMatchesSuite) TestMatchSimpleMessages(c *gc.C) { log := []loggo.Entry{ {Level: loggo.INFO, Message: "foo bar"}, {Level: loggo.INFO, Message: "12345"}, } c.Check(log, jc.LogMatches, jc.SimpleMessages{ {loggo.INFO, "foo bar"}, {loggo.INFO, "12345"}, }) c.Check(log, jc.LogMatches, jc.SimpleMessages{ {loggo.INFO, "foo .*"}, {loggo.INFO, "12345"}, }) // UNSPECIFIED means we don't care what the level is, // just check the message string matches. c.Check(log, jc.LogMatches, jc.SimpleMessages{ {loggo.UNSPECIFIED, "foo .*"}, {loggo.INFO, "12345"}, }) c.Check(log, gc.Not(jc.LogMatches), jc.SimpleMessages{ {loggo.INFO, "foo bar"}, {loggo.DEBUG, "12345"}, }) } func (s *LogMatchesSuite) TestMatchStrings(c *gc.C) { log := []loggo.Entry{ {Level: loggo.INFO, Message: "foo bar"}, {Level: loggo.INFO, Message: "12345"}, } c.Check(log, jc.LogMatches, []string{"foo bar", "12345"}) c.Check(log, jc.LogMatches, []string{"foo .*", "12345"}) c.Check(log, gc.Not(jc.LogMatches), []string{"baz", "bing"}) } func (s *LogMatchesSuite) TestMatchInexact(c *gc.C) { log := []loggo.Entry{ {Level: loggo.INFO, Message: "foo bar"}, {Level: loggo.INFO, Message: "baz"}, {Level: loggo.DEBUG, Message: "12345"}, {Level: loggo.ERROR, Message: "12345"}, {Level: loggo.INFO, Message: "67890"}, } c.Check(log, jc.LogMatches, []string{"foo bar", "12345"}) c.Check(log, jc.LogMatches, []string{"foo .*", "12345"}) c.Check(log, jc.LogMatches, []string{"foo .*", "67890"}) c.Check(log, jc.LogMatches, []string{"67890"}) // Matches are always left-most after the previous match. c.Check(log, jc.LogMatches, []string{".*", "baz"}) c.Check(log, jc.LogMatches, []string{"foo bar", ".*", "12345"}) c.Check(log, jc.LogMatches, []string{"foo bar", ".*", "67890"}) // Order is important: 67890 advances to the last item in obtained, // and so there's nothing after to match against ".*". c.Check(log, gc.Not(jc.LogMatches), []string{"67890", ".*"}) // ALL specified patterns MUST match in the order given. c.Check(log, gc.Not(jc.LogMatches), []string{".*", "foo bar"}) // Check that levels are matched. c.Check(log, jc.LogMatches, []jc.SimpleMessage{ {loggo.UNSPECIFIED, "12345"}, {loggo.UNSPECIFIED, "12345"}, }) c.Check(log, jc.LogMatches, []jc.SimpleMessage{ {loggo.DEBUG, "12345"}, {loggo.ERROR, "12345"}, }) c.Check(log, jc.LogMatches, []jc.SimpleMessage{ {loggo.DEBUG, "12345"}, {loggo.INFO, ".*"}, }) c.Check(log, gc.Not(jc.LogMatches), []jc.SimpleMessage{ {loggo.DEBUG, "12345"}, {loggo.INFO, ".*"}, {loggo.UNSPECIFIED, ".*"}, }) } func (s *LogMatchesSuite) TestFromLogMatches(c *gc.C) { tw := &loggo.TestWriter{} _, err := loggo.ReplaceDefaultWriter(tw) c.Assert(err, gc.IsNil) defer loggo.ResetWriters() logger := loggo.GetLogger("test") logger.SetLogLevel(loggo.DEBUG) logger.Infof("foo") logger.Debugf("bar") logger.Tracef("hidden") c.Check(tw.Log(), jc.LogMatches, []string{"foo", "bar"}) c.Check(tw.Log(), gc.Not(jc.LogMatches), []string{"foo", "bad"}) c.Check(tw.Log(), gc.Not(jc.LogMatches), []jc.SimpleMessage{ {loggo.INFO, "foo"}, {loggo.INFO, "bar"}, }) } func (s *LogMatchesSuite) TestLogMatchesOnlyAcceptsSliceTestLogValues(c *gc.C) { obtained := []string{"banana"} // specifically not []loggo.TestLogValues expected := jc.SimpleMessages{} result, err := jc.LogMatches.Check([]interface{}{obtained, expected}, nil) c.Assert(result, gc.Equals, false) c.Assert(err, gc.Equals, "Obtained value must be of type []loggo.Entry or SimpleMessage") } func (s *LogMatchesSuite) TestLogMatchesOnlyAcceptsStringOrSimpleMessages(c *gc.C) { obtained := []loggo.Entry{ {Level: loggo.INFO, Message: "foo bar"}, {Level: loggo.INFO, Message: "baz"}, {Level: loggo.DEBUG, Message: "12345"}, } expected := "totally wrong" result, err := jc.LogMatches.Check([]interface{}{obtained, expected}, nil) c.Assert(result, gc.Equals, false) c.Assert(err, gc.Equals, "Expected value must be of type []string or []SimpleMessage") } func (s *LogMatchesSuite) TestLogMatchesFailsOnInvalidRegex(c *gc.C) { var obtained interface{} = []loggo.Entry{{Level: loggo.INFO, Message: "foo bar"}} var expected interface{} = []string{"[]foo"} result, err := jc.LogMatches.Check([]interface{}{obtained, expected}, nil /* unused */) c.Assert(result, gc.Equals, false) c.Assert(err, gc.Equals, "bad message regexp \"[]foo\": error parsing regexp: missing closing ]: `[]foo`") } testing-1.2.0/checkers/multichecker.go000066400000000000000000000137121456110213600177670ustar00rootroot00000000000000// Copyright 2020 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers import ( "fmt" "go/ast" "go/parser" "regexp" "strings" "sync" gc "gopkg.in/check.v1" ) // MultiChecker is a deep checker that by default matches for equality. // But checks can be overriden based on path (either explicit match or regexp) type MultiChecker struct { *gc.CheckerInfo checks map[string]checkerWithArgs matchChecks []matchCheck } type checkerWithArgs interface { gc.Checker Args() []interface{} } type matchCheck interface { checkerWithArgs MatchString(string) bool WantTopLevel() bool } type multiCheck struct { gc.Checker args []interface{} } func (m *multiCheck) Args() []interface{} { return m.args } type regexCheck struct { multiCheck *regexp.Regexp } func (r *regexCheck) WantTopLevel() bool { return false } type astCheck struct { multiCheck astExpr ast.Expr } func (a *astCheck) WantTopLevel() bool { return true } // NewMultiChecker creates a MultiChecker which is a deep checker that by default matches for equality. // But checks can be overriden based on path (either explicit match or regexp) func NewMultiChecker() *MultiChecker { return &MultiChecker{ CheckerInfo: &gc.CheckerInfo{Name: "MultiChecker", Params: []string{"obtained", "expected"}}, checks: make(map[string]checkerWithArgs), } } // Add an explict checker by path. func (checker *MultiChecker) Add(path string, c gc.Checker, args ...interface{}) *MultiChecker { checker.checks[path] = &multiCheck{ Checker: c, args: args, } return checker } // AddRegex exception which matches path with regex. func (checker *MultiChecker) AddRegex(pathRegex string, c gc.Checker, args ...interface{}) *MultiChecker { checker.matchChecks = append(checker.matchChecks, ®exCheck{ multiCheck: multiCheck{ Checker: c, args: args, }, Regexp: regexp.MustCompile("^" + pathRegex + "$"), }) return checker } // AddExpr exception which matches path with go expression. Use _ for wildcard. // The top level or root value must be a _ when using expression. func (checker *MultiChecker) AddExpr(expr string, c gc.Checker, args ...interface{}) *MultiChecker { astExpr, err := parser.ParseExpr(expr) if err != nil { panic(err) } checker.matchChecks = append(checker.matchChecks, &astCheck{ multiCheck: multiCheck{ Checker: c, args: args, }, astExpr: astExpr, }) return checker } // topLevel is a substitute for the top level or root object. // We use an unlikely value to provide backwards compatability with previous deep equals // behaviour. It is stripped out before any errors are printed. const topLevel = "🔝" // Check for go check Checker interface. func (checker *MultiChecker) Check(params []interface{}, names []string) (result bool, errStr string) { customCheckFunc := func(path string, a1 interface{}, a2 interface{}) (useDefault bool, equal bool, err error) { var mc checkerWithArgs if c, ok := checker.checks[strings.Replace(path, topLevel, "", 1)]; ok { mc = c } else { for _, v := range checker.matchChecks { pathCopy := path if !v.WantTopLevel() { pathCopy = strings.Replace(pathCopy, topLevel, "", 1) } if v.MatchString(pathCopy) { mc = v break } } } if mc == nil { return true, false, nil } params := append([]interface{}{a1}, mc.Args()...) info := mc.Info() if len(params) < len(info.Params) { return false, false, fmt.Errorf("Wrong number of parameters for %s: want %d, got %d", info.Name, len(info.Params), len(params)+1) } // Copy since it may be mutated by Check. names := append([]string{}, info.Params...) // Trim to the expected params len. params = params[:len(info.Params)] // Perform substitution for i, v := range params { if v == ExpectedValue { params[i] = a2 } } result, errStr := mc.Check(params, names) if result { return false, true, nil } path = strings.Replace(path, topLevel, "", 1) if path == "" { path = "top level" } return false, false, fmt.Errorf("mismatch at %s: %s", path, errStr) } if ok, err := DeepEqualWithCustomCheck(params[0], params[1], customCheckFunc); !ok { return false, err.Error() } return true, "" } // ExpectedValue if passed to MultiChecker.Add or MultiChecker.AddRegex, will be substituded with the expected value. var ExpectedValue = &struct{}{} var ( astCache = make(map[string]ast.Expr) astCacheLock = sync.Mutex{} ) func (a *astCheck) MatchString(expr string) bool { expr = strings.Replace(expr, topLevel, "_", 1) astCacheLock.Lock() astExpr, ok := astCache[expr] astCacheLock.Unlock() if !ok { var err error astExpr, err = parser.ParseExpr(expr) if err != nil { panic(err) } astCacheLock.Lock() astCache[expr] = astExpr astCacheLock.Unlock() } return matchAstExpr(a.astExpr, astExpr) } func matchAstExpr(expected, obtained ast.Expr) bool { switch expr := expected.(type) { case *ast.IndexExpr: x, ok := obtained.(*ast.IndexExpr) if !ok { return false } if !matchAstExpr(expr.Index, x.Index) { return false } if !matchAstExpr(expr.X, x.X) { return false } case *ast.ParenExpr: x, ok := obtained.(*ast.ParenExpr) if !ok { return false } if !matchAstExpr(expr.X, x.X) { return false } case *ast.StarExpr: x, ok := obtained.(*ast.StarExpr) if !ok { return false } if !matchAstExpr(expr.X, x.X) { return false } case *ast.SelectorExpr: x, ok := obtained.(*ast.SelectorExpr) if !ok { return false } if !matchAstExpr(expr.X, x.X) { return false } if !matchAstExpr(expr.Sel, x.Sel) { return false } case *ast.Ident: if expr.Name == "_" { // Wildcard return true } x, ok := obtained.(*ast.Ident) if !ok { return false } if expr.Name != x.Name { return false } case *ast.BasicLit: x, ok := obtained.(*ast.BasicLit) if !ok { return false } if expr.Value != x.Value { return false } default: panic(fmt.Sprintf("unknown type %#v", expected)) } return true } testing-1.2.0/checkers/multichecker_test.go000066400000000000000000000072351456110213600210310ustar00rootroot00000000000000package checkers_test import ( jc "github.com/juju/testing/checkers" gc "gopkg.in/check.v1" ) type MultiCheckerSuite struct{} var _ = gc.Suite(&MultiCheckerSuite{}) func (s *MultiCheckerSuite) TestDeepEquals(c *gc.C) { for i, test := range deepEqualTests { c.Logf("test %d. %v == %v is %v", i, test.a, test.b, test.eq) result, msg := jc.NewMultiChecker().Check([]interface{}{test.a, test.b}, nil) c.Check(result, gc.Equals, test.eq) if test.eq { c.Check(msg, gc.Equals, "") } else { c.Check(msg, gc.Not(gc.Equals), "") } } } func (s *MultiCheckerSuite) TestArray(c *gc.C) { a1 := []string{"a", "b", "c"} a2 := []string{"a", "bbb", "c"} checker := jc.NewMultiChecker().Add("[1]", jc.Ignore) c.Check(a1, checker, a2) } func (s *MultiCheckerSuite) TestMap(c *gc.C) { a1 := map[string]string{"a": "a", "b": "b", "c": "c"} a2 := map[string]string{"a": "a", "b": "bbbb", "c": "c"} checker := jc.NewMultiChecker().Add(`["b"]`, jc.Ignore) c.Check(a1, checker, a2) } func (s *MultiCheckerSuite) TestRegexArray(c *gc.C) { a1 := []string{"a", "b", "c"} a2 := []string{"a", "bbb", "ccc"} checker := jc.NewMultiChecker().AddRegex("\\[[1-2]\\]", jc.Ignore) c.Check(a1, checker, a2) } func (s *MultiCheckerSuite) TestRegexMap(c *gc.C) { a1 := map[string]string{"a": "a", "b": "b", "c": "c"} a2 := map[string]string{"a": "aaaa", "b": "bbbb", "c": "cccc"} checker := jc.NewMultiChecker().AddRegex(`\[".*"\]`, jc.Ignore) c.Check(a1, checker, a2) } func (s *MultiCheckerSuite) TestArrayArraysUnordered(c *gc.C) { a1 := [][]string{{"a", "b", "c"}, {"c", "d", "e"}} a2 := [][]string{{"a", "b", "c"}, {}} checker := jc.NewMultiChecker().Add("[1]", jc.SameContents, []string{"e", "c", "d"}) c.Check(a1, checker, a2) } func (s *MultiCheckerSuite) TestArrayArraysUnorderedWithExpected(c *gc.C) { a1 := [][]string{{"a", "b", "c"}, {"c", "d", "e"}} a2 := [][]string{{"a", "b", "c"}, {"e", "c", "d"}} checker := jc.NewMultiChecker().Add("[1]", jc.SameContents, jc.ExpectedValue) c.Check(a1, checker, a2) } type pod struct { A int a int B bool b bool C string c string } func (s *MultiCheckerSuite) TestPOD(c *gc.C) { a1 := pod{1, 2, true, true, "a", "a"} a2 := pod{2, 3, false, false, "b", "b"} checker := jc.NewMultiChecker(). Add(".A", jc.Ignore). Add(".a", jc.Ignore). Add(".B", jc.Ignore). Add(".b", jc.Ignore). Add(".C", jc.Ignore). Add(".c", jc.Ignore) c.Check(a1, checker, a2) } func (s *MultiCheckerSuite) TestExprMap(c *gc.C) { a1 := map[string]string{"a": "a", "b": "b", "c": "c"} a2 := map[string]string{"a": "aaaa", "b": "bbbb", "c": "cccc"} checker := jc.NewMultiChecker().AddExpr(`_[_]`, jc.Ignore) c.Check(a1, checker, a2) } type complexA struct { complexB A int C []int D map[string]string E *complexE F **complexF } type complexB struct { B string b string } type complexE struct { E string } type complexF struct { F []string } func (s *MultiCheckerSuite) TestExprComplex(c *gc.C) { f1 := &complexF{ F: []string{"a", "b"}, } a1 := complexA{ complexB: complexB{ B: "wow", b: "wow", }, A: 5, C: []int{0, 1, 2, 3, 4, 5}, D: map[string]string{"a": "b"}, E: &complexE{E: "E"}, F: &f1, } f2 := &complexF{ F: []string{"c", "d"}, } a2 := complexA{ complexB: complexB{ B: "cool", b: "cool", }, A: 19, C: []int{5, 4, 3, 2, 1, 0}, D: map[string]string{"b": "a"}, E: &complexE{E: "EEEEEEEEE"}, F: &f2, } checker := jc.NewMultiChecker(). AddExpr(`_.complexB.B`, jc.Ignore). AddExpr(`_.complexB.b`, jc.Ignore). AddExpr(`_.A`, jc.Ignore). AddExpr(`_.C[_]`, jc.Ignore). AddExpr(`_.D`, jc.Ignore). AddExpr(`(*_.E)`, jc.Ignore). AddExpr(`(*(*_.F)).F[_]`, jc.Ignore) c.Check(a1, checker, a2) } testing-1.2.0/checkers/relop.go000066400000000000000000000041121456110213600164230ustar00rootroot00000000000000// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers import ( "fmt" "reflect" gc "gopkg.in/check.v1" ) // GreaterThan checker type greaterThanChecker struct { *gc.CheckerInfo } var GreaterThan gc.Checker = &greaterThanChecker{ &gc.CheckerInfo{Name: "GreaterThan", Params: []string{"obtained", "expected"}}, } func (checker *greaterThanChecker) Check(params []interface{}, names []string) (result bool, error string) { defer func() { if v := recover(); v != nil { result = false error = fmt.Sprint(v) } }() p0value := reflect.ValueOf(params[0]) p1value := reflect.ValueOf(params[1]) switch p0value.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return p0value.Int() > p1value.Int(), "" case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return p0value.Uint() > p1value.Uint(), "" case reflect.Float32, reflect.Float64: return p0value.Float() > p1value.Float(), "" default: } return false, fmt.Sprintf("obtained value %s:%#v not supported", p0value.Kind(), params[0]) } // LessThan checker type lessThanChecker struct { *gc.CheckerInfo } var LessThan gc.Checker = &lessThanChecker{ &gc.CheckerInfo{Name: "LessThan", Params: []string{"obtained", "expected"}}, } func (checker *lessThanChecker) Check(params []interface{}, names []string) (result bool, error string) { defer func() { if v := recover(); v != nil { result = false error = fmt.Sprint(v) } }() p0value := reflect.ValueOf(params[0]) p1value := reflect.ValueOf(params[1]) switch p0value.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return p0value.Int() < p1value.Int(), "" case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return p0value.Uint() < p1value.Uint(), "" case reflect.Float32, reflect.Float64: return p0value.Float() < p1value.Float(), "" default: } return false, fmt.Sprintf("obtained value %s:%#v not supported", p0value.Kind(), params[0]) } testing-1.2.0/checkers/relop_test.go000066400000000000000000000017531456110213600174720ustar00rootroot00000000000000// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers_test import ( gc "gopkg.in/check.v1" jc "github.com/juju/testing/checkers" ) type RelopSuite struct{} var _ = gc.Suite(&RelopSuite{}) func (s *RelopSuite) TestGreaterThan(c *gc.C) { c.Assert(45, jc.GreaterThan, 42) c.Assert(2.25, jc.GreaterThan, 1.0) c.Assert(42, gc.Not(jc.GreaterThan), 42) c.Assert(10, gc.Not(jc.GreaterThan), 42) result, msg := jc.GreaterThan.Check([]interface{}{"Hello", "World"}, nil) c.Assert(result, jc.IsFalse) c.Assert(msg, gc.Equals, `obtained value string:"Hello" not supported`) } func (s *RelopSuite) TestLessThan(c *gc.C) { c.Assert(42, jc.LessThan, 45) c.Assert(1.0, jc.LessThan, 2.25) c.Assert(42, gc.Not(jc.LessThan), 42) c.Assert(42, gc.Not(jc.LessThan), 10) result, msg := jc.LessThan.Check([]interface{}{"Hello", "World"}, nil) c.Assert(result, jc.IsFalse) c.Assert(msg, gc.Equals, `obtained value string:"Hello" not supported`) } testing-1.2.0/checkers/time.go000066400000000000000000000033201456110213600162400ustar00rootroot00000000000000// Copyright 2022 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers import ( "fmt" "math" "reflect" "time" gc "gopkg.in/check.v1" ) type timeCompareChecker struct { *gc.CheckerInfo compareFunc func(time.Time, time.Time) bool } // After checks whether the obtained time.Time is After the want time.Time. var After gc.Checker = &timeCompareChecker{ CheckerInfo: &gc.CheckerInfo{Name: "After", Params: []string{"obtained", "want"}}, compareFunc: func(t1, t2 time.Time) bool { return t1.After(t2) }, } // Before checks whether the obtained time.Time is Before the want time.Time. var Before gc.Checker = &timeCompareChecker{ CheckerInfo: &gc.CheckerInfo{Name: "Before", Params: []string{"obtained", "want"}}, compareFunc: func(t1, t2 time.Time) bool { return t1.Before(t2) }, } // Almost checks whether the obtained time.Time is within 1s of the want time.Time. var Almost gc.Checker = &timeCompareChecker{ CheckerInfo: &gc.CheckerInfo{Name: "Almost", Params: []string{"obtained", "want"}}, compareFunc: func(t1, t2 time.Time) bool { return math.Abs(t1.Sub(t2).Seconds()) <= 1.0 }, } func (checker *timeCompareChecker) Check(params []interface{}, names []string) (result bool, error string) { if len(params) != 2 { return false, fmt.Sprintf("expected 2 parameters, received %d", len(params)) } t1, ok := params[0].(time.Time) if !ok { return false, fmt.Sprintf("obtained param: expected type time.Time, received type %s", reflect.ValueOf(params[0]).Type()) } t2, ok := params[1].(time.Time) if !ok { return false, fmt.Sprintf("want param: expected type time.Time, received type %s", reflect.ValueOf(params[1]).Type()) } return checker.compareFunc(t1, t2), "" } testing-1.2.0/checkers/time_test.go000066400000000000000000000046541456110213600173120ustar00rootroot00000000000000// Copyright 2022 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers_test import ( "time" gc "gopkg.in/check.v1" jc "github.com/juju/testing/checkers" ) type TimeSuite struct{} var _ = gc.Suite(&TimeSuite{}) func (s *TimeSuite) TestBefore(c *gc.C) { now := time.Now() c.Assert(now, jc.Before, now.Add(time.Second)) c.Assert(now, gc.Not(jc.Before), now.Add(-time.Second)) result, msg := jc.Before.Check([]interface{}{time.Time{}}, nil) c.Assert(result, gc.Equals, false) c.Check(msg, gc.Equals, `expected 2 parameters, received 1`) result, msg = jc.Before.Check([]interface{}{42, time.Time{}}, nil) c.Assert(result, gc.Equals, false) c.Assert(msg, gc.Equals, `obtained param: expected type time.Time, received type int`) result, msg = jc.Before.Check([]interface{}{time.Time{}, "wow"}, nil) c.Assert(result, gc.Equals, false) c.Assert(msg, gc.Matches, `want param: expected type time.Time, received type string`) } func (s *TimeSuite) TestAfter(c *gc.C) { now := time.Now() c.Assert(now, gc.Not(jc.After), now.Add(time.Second)) c.Assert(now, jc.After, now.Add(-time.Second)) result, msg := jc.After.Check([]interface{}{time.Time{}}, nil) c.Assert(result, gc.Equals, false) c.Check(msg, gc.Equals, `expected 2 parameters, received 1`) result, msg = jc.After.Check([]interface{}{42, time.Time{}}, nil) c.Assert(result, gc.Equals, false) c.Assert(msg, gc.Equals, `obtained param: expected type time.Time, received type int`) result, msg = jc.After.Check([]interface{}{time.Time{}, "wow"}, nil) c.Assert(result, gc.Equals, false) c.Assert(msg, gc.Matches, `want param: expected type time.Time, received type string`) } func (s *TimeSuite) TestAlmost(c *gc.C) { now := time.Now() c.Assert(now, gc.Not(jc.Almost), now.Add(1001*time.Millisecond)) c.Assert(now, jc.Almost, now.Add(-time.Second)) c.Assert(now, jc.Almost, now.Add(time.Second)) result, msg := jc.Almost.Check([]interface{}{time.Time{}}, nil) c.Assert(result, gc.Equals, false) c.Check(msg, gc.Equals, `expected 2 parameters, received 1`) result, msg = jc.Almost.Check([]interface{}{42, time.Time{}}, nil) c.Assert(result, gc.Equals, false) c.Assert(msg, gc.Equals, `obtained param: expected type time.Time, received type int`) result, msg = jc.Almost.Check([]interface{}{time.Time{}, "wow"}, nil) c.Assert(result, gc.Equals, false) c.Assert(msg, gc.Matches, `want param: expected type time.Time, received type string`) } testing-1.2.0/cleanup.go000066400000000000000000000105151456110213600151460ustar00rootroot00000000000000// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing import ( "os/exec" gc "gopkg.in/check.v1" ) // CleanupSuite adds the ability to add cleanup functions that are called // during either test tear down or suite tear down depending on the method // called. type CleanupSuite struct { testStack []func(*gc.C) suiteStack []func(*gc.C) origSuite *CleanupSuite testsStarted bool inTest bool tornDown bool } func (s *CleanupSuite) SetUpSuite(c *gc.C) { s.suiteStack = nil s.testStack = nil s.origSuite = s s.testsStarted = false s.inTest = false s.tornDown = false } func (s *CleanupSuite) TearDownSuite(c *gc.C) { s.callStack(c, s.suiteStack) s.suiteStack = nil s.origSuite = nil s.tornDown = true } func (s *CleanupSuite) SetUpTest(c *gc.C) { s.testStack = nil s.testsStarted = true s.inTest = true } func (s *CleanupSuite) TearDownTest(c *gc.C) { s.callStack(c, s.testStack) s.testStack = nil s.inTest = false } func (s *CleanupSuite) callStack(c *gc.C, stack []func(*gc.C)) { for i := len(stack) - 1; i >= 0; i-- { stack[i](c) } } // AddCleanup pushes the cleanup function onto the stack of functions to be // called during TearDownTest or TearDownSuite. TearDownTest will be used if // SetUpTest has already been called, else we will use TearDownSuite func (s *CleanupSuite) AddCleanup(cleanup func(*gc.C)) { if s.origSuite == nil { // This is either called before SetUpSuite or after // TearDownSuite. Either way, we can't really trust that we're // going to call Cleanup correctly. if s.tornDown { panic("unsafe to call AddCleanup after TearDownSuite") } else { panic("unsafe to call AddCleanup before SetUpSuite") } } if s != s.origSuite { // If you write a test like: // func (s MySuite) TestFoo(c *gc.C) { // s.AddCleanup(foo) // } // The AddCleanup call is unsafe because it modifes // s.origSuite but that object disappears once TestFoo // returns. So you have to use: // func (s *MySuite) TestFoo(c *gc.C) if you want the Cleanup // funcs. panic("unsafe to call AddCleanup from non pointer receiver test") } if !s.inTest { if s.testsStarted { // This indicates that we are not currently in a test // (inTest is false), but that we have already run a // test for this test suite (testStarted is true). // Making a Suite-level change here means that only // some of the tests in the suite will see the change, // which means it *isn't* a Suite (applies to all // tests) level change. panic("unsafe to call AddCleanup after a test has been torn down" + " before a new test has been set up" + " (Suite level changes only make sense before first test is run)") } // We either haven't called SetUpTest or we've already called // TearDownTest, consider this a Suite level cleanup. s.suiteStack = append(s.suiteStack, cleanup) return } s.testStack = append(s.testStack, cleanup) } // PatchEnvironment sets the environment variable 'name' the the value passed // in. The old value is saved and returned to the original value at test tear // down time using a cleanup function. func (s *CleanupSuite) PatchEnvironment(name, value string) { restore := PatchEnvironment(name, value) s.AddCleanup(func(*gc.C) { restore() }) } // PatchEnvPathPrepend prepends the given path to the environment $PATH and restores the // original path on test teardown. func (s *CleanupSuite) PatchEnvPathPrepend(dir string) { restore := PatchEnvPathPrepend(dir) s.AddCleanup(func(*gc.C) { restore() }) } // PatchValue sets the 'dest' variable the the value passed in. The old value // is saved and returned to the original value at test tear down time using a // cleanup function. The value must be assignable to the element type of the // destination. func (s *CleanupSuite) PatchValue(dest, value interface{}) { restore := PatchValue(dest, value) s.AddCleanup(func(*gc.C) { restore() }) } // HookCommandOutput calls the package function of the same name to mock out // the result of a particular comand execution, and will call the restore // function on test teardown. func (s *CleanupSuite) HookCommandOutput( outputFunc *func(cmd *exec.Cmd) ([]byte, error), output []byte, err error, ) <-chan *exec.Cmd { result, restore := HookCommandOutput(outputFunc, output, err) s.AddCleanup(func(*gc.C) { restore() }) return result } testing-1.2.0/cleanup_test.go000066400000000000000000000111511456110213600162020ustar00rootroot00000000000000// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing_test import ( "os" gc "gopkg.in/check.v1" "github.com/juju/testing" ) type cleanupSuite struct { testing.CleanupSuite } var _ = gc.Suite(&cleanupSuite{}) func (s *cleanupSuite) TestTearDownSuiteEmpty(c *gc.C) { // The suite stack is empty initially, check we can tear that down. s.TearDownSuite(c) s.SetUpSuite(c) } func (s *cleanupSuite) TestTearDownTestEmpty(c *gc.C) { // The test stack is empty initially, check we can tear that down. s.TearDownTest(c) s.SetUpTest(c) } func (s *cleanupSuite) TestAddCleanup(c *gc.C) { order := []string{} s.AddCleanup(func(*gc.C) { order = append(order, "first") }) s.AddCleanup(func(*gc.C) { order = append(order, "second") }) s.TearDownTest(c) c.Assert(order, gc.DeepEquals, []string{"second", "first"}) // SetUpTest resets the cleanup stack, this stops the cleanup functions // being called again. s.SetUpTest(c) } func (s *cleanupSuite) TestPatchEnvironment(c *gc.C) { const envName = "TESTING_PATCH_ENVIRONMENT" // remember the old value, and set it to something we can check oldValue := os.Getenv(envName) os.Setenv(envName, "initial") s.PatchEnvironment(envName, "new value") // Using check to make sure the environment gets set back properly in the test. c.Check(os.Getenv(envName), gc.Equals, "new value") s.TearDownTest(c) c.Check(os.Getenv(envName), gc.Equals, "initial") // SetUpTest resets the cleanup stack, this stops the cleanup functions // being called again. s.SetUpTest(c) // explicitly return the envName to the old value os.Setenv(envName, oldValue) } func (s *cleanupSuite) TestPatchValueInt(c *gc.C) { i := 42 s.PatchValue(&i, 0) c.Assert(i, gc.Equals, 0) s.TearDownTest(c) c.Assert(i, gc.Equals, 42) // SetUpTest resets the cleanup stack, this stops the cleanup functions // being called again. s.SetUpTest(c) } func (s *cleanupSuite) TestPatchValueFunction(c *gc.C) { function := func() string { return "original" } s.PatchValue(&function, func() string { return "patched" }) c.Assert(function(), gc.Equals, "patched") s.TearDownTest(c) c.Assert(function(), gc.Equals, "original") // SetUpTest resets the cleanup stack, this stops the cleanup functions // being called again. s.SetUpTest(c) } // noopCleanup is a simple function that does nothing that can be passed to // AddCleanup func noopCleanup(*gc.C) { } func (s cleanupSuite) TestAddCleanupPanicIfUnsafe(c *gc.C) { // It is unsafe to call AddCleanup when the test itself is not a // pointer receiver, because AddCleanup modifies the s.testStack // attribute, but in a non-pointer receiver, that object is lost when // the Test function returns. // This Test must, itself, be a non pointer receiver to trigger this c.Assert(func() { s.AddCleanup(noopCleanup) }, gc.PanicMatches, "unsafe to call AddCleanup from non pointer receiver test") } type cleanupSuiteAndTestLifetimes struct { } var _ = gc.Suite(&cleanupSuiteAndTestLifetimes{}) func (s *cleanupSuiteAndTestLifetimes) TestAddCleanupBeforeSetUpSuite(c *gc.C) { suite := &testing.CleanupSuite{} c.Assert(func() { suite.AddCleanup(noopCleanup) }, gc.PanicMatches, "unsafe to call AddCleanup before SetUpSuite") suite.SetUpSuite(c) suite.SetUpTest(c) suite.TearDownTest(c) suite.TearDownSuite(c) } func (s *cleanupSuiteAndTestLifetimes) TestAddCleanupAfterTearDownSuite(c *gc.C) { suite := &testing.CleanupSuite{} suite.SetUpSuite(c) suite.SetUpTest(c) suite.TearDownTest(c) suite.TearDownSuite(c) c.Assert(func() { suite.AddCleanup(noopCleanup) }, gc.PanicMatches, "unsafe to call AddCleanup after TearDownSuite") } func (s *cleanupSuiteAndTestLifetimes) TestAddCleanupMixedSuiteAndTest(c *gc.C) { calls := []string{} suite := &testing.CleanupSuite{} suite.SetUpSuite(c) suite.AddCleanup(func(*gc.C) { calls = append(calls, "before SetUpTest") }) suite.SetUpTest(c) suite.AddCleanup(func(*gc.C) { calls = append(calls, "during Test1") }) suite.TearDownTest(c) c.Check(calls, gc.DeepEquals, []string{ "during Test1", }) c.Assert(func() { suite.AddCleanup(noopCleanup) }, gc.PanicMatches, "unsafe to call AddCleanup after a test has been torn down"+ " before a new test has been set up"+ " \\(Suite level changes only make sense before first test is run\\)") suite.SetUpTest(c) suite.AddCleanup(func(*gc.C) { calls = append(calls, "during Test2") }) suite.TearDownTest(c) c.Check(calls, gc.DeepEquals, []string{ "during Test1", "during Test2", }) suite.TearDownSuite(c) c.Check(calls, gc.DeepEquals, []string{ "during Test1", "during Test2", "before SetUpTest", }) } testing-1.2.0/cmd.go000066400000000000000000000261651456110213600142720ustar00rootroot00000000000000// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing import ( "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" "github.com/juju/utils/v4" gc "gopkg.in/check.v1" jc "github.com/juju/testing/checkers" ) var HookChannelSize = 10 // HookCommandOutput intercepts CommandOutput to a function that passes the // actual command and it's output back via a channel, and returns the error // passed into this function. It also returns a cleanup function so you can // restore the original function func HookCommandOutput( outputFunc *func(cmd *exec.Cmd) ([]byte, error), output []byte, err error) (<-chan *exec.Cmd, func()) { cmdChan := make(chan *exec.Cmd, HookChannelSize) origCommandOutput := *outputFunc cleanup := func() { close(cmdChan) *outputFunc = origCommandOutput } *outputFunc = func(cmd *exec.Cmd) ([]byte, error) { cmdChan <- cmd return output, err } return cmdChan, cleanup } const ( // EchoQuotedArgs is a simple bash script that prints out the // basename of the command followed by the args as quoted strings. // If a ; separated list of exit codes is provided in $name.exitcodes // then it will return them in turn over multiple calls. If // $name.exitcodes does not exist, or the list runs out, return 0. EchoQuotedArgsUnix = `#!/bin/bash --norc name=` + "`basename $0`" + ` argfile="$0.out" exitcodesfile="$0.exitcodes" printf "%s" $name | tee -a $argfile for arg in "$@"; do printf " '%s'" "$arg" | tee -a $argfile done printf "\n" | tee -a $argfile if [ -f $exitcodesfile ] then exitcodes=$(cat $exitcodesfile) arr=(${exitcodes/;/ }) echo ${arr[1]} | tee $exitcodesfile exit ${arr[0]} fi ` EchoQuotedArgsWindows = `@echo off setlocal enabledelayedexpansion set list=%0 set argCount=0 set argfile=%~f0.out set exitcodesfile=%~f0.exitcodes for %%x in (%*) do ( set /A argCount+=1 set "argVec[!argCount!]=%%~x" ) for /L %%i in (1,1,%argCount%) do set list=!list! '!argVec[%%i]!' IF exist %exitcodesfile% ( FOR /F "tokens=1* delims=;" %%i IN (%exitcodesfile%) DO ( set exitcode=%%i IF NOT [%%j]==[] ( echo %%j > %exitcodesfile% ) ELSE ( del %exitcodesfile% ) ) ) echo %list%>> %argfile% exit /B %exitcode% ` ) // EnvironmentPatcher is an interface that requires just one method: // PatchEnvironment. type EnvironmentPatcher interface { PatchEnvironment(name, value string) } // PatchExecutable creates an executable called 'execName' in a new test // directory and that directory is added to the path. func PatchExecutable(c *gc.C, patcher EnvironmentPatcher, execName, script string, exitCodes ...int) { dir := c.MkDir() patcher.PatchEnvironment("PATH", joinPathLists(dir, os.Getenv("PATH"))) var filename string switch runtime.GOOS { case "windows": filename = filepath.Join(dir, execName+".bat") default: filename = filepath.Join(dir, execName) } err := ioutil.WriteFile(filename, []byte(script), 0755) c.Assert(err, gc.IsNil) if len(exitCodes) > 0 { filename := filename + ".exitcodes" codes := make([]string, len(exitCodes)) for i, code := range exitCodes { codes[i] = strconv.Itoa(code) } s := strings.Join(codes, ";") + ";" err = ioutil.WriteFile(filename, []byte(s), 0644) c.Assert(err, gc.IsNil) } } // PatchExecutableThrowError is needed to test cases in which we expect exit // codes from executables called from the system path func PatchExecutableThrowError(c *gc.C, patcher EnvironmentPatcher, execName string, exitCode int) { switch runtime.GOOS { case "windows": script := fmt.Sprintf(`@echo off setlocal enabledelayedexpansion echo failing exit /b %d REM see %%ERRORLEVEL%% for last exit code like $? on linux `, exitCode) PatchExecutable(c, patcher, execName, script) default: script := fmt.Sprintf(`#!/bin/bash --norc echo failing exit %d `, exitCode) PatchExecutable(c, patcher, execName, script) } } // PatchExecutableAsEchoArgs creates an executable called 'execName' in a new // test directory and that directory is added to the path. The content of the // script is 'EchoQuotedArgs', and the args file is removed using a cleanup // function. func PatchExecutableAsEchoArgs(c *gc.C, patcher EnvironmentPatcher, execName string, exitCodes ...int) { switch runtime.GOOS { case "windows": PatchExecutable(c, patcher, execName, EchoQuotedArgsWindows, exitCodes...) default: PatchExecutable(c, patcher, execName, EchoQuotedArgsUnix, exitCodes...) } } // AssertEchoArgs is used to check the args from an execution of a command // that has been patched using PatchExecutable containing EchoQuotedArgs. func AssertEchoArgs(c *gc.C, execName string, args ...string) { // Create expected output string expected := execName for _, arg := range args { expected = fmt.Sprintf("%s %s", expected, utils.ShQuote(arg)) } actual := ReadEchoArgs(c, execName) c.Assert(actual, gc.Equals, expected) } // ReadEchoArgs is used to read the args from an execution of a command // that has been patched using PatchExecutable containing EchoQuotedArgs. func ReadEchoArgs(c *gc.C, execName string) string { execPath, err := exec.LookPath(execName) c.Assert(err, jc.ErrorIsNil) // Read in entire argument log file content, err := ioutil.ReadFile(execPath + ".out") c.Assert(err, jc.ErrorIsNil) lines := strings.Split(string(content), "\n") actual := strings.TrimSuffix(lines[0], "\r") // Write out the remaining lines for the next check content = []byte(strings.Join(lines[1:], "\n")) err = ioutil.WriteFile(execPath+".out", content, 0644) // or just call this filename somewhere, once. return actual } // PatchExecHelper is a type that helps you patch out calls to executables by // patching out the exec.Command function that creates the exec.Cmd to call // them. This is very similar to PatchExecutable above, except it works on // windows exe files, is a lot easier to control stderr and stdout, doesn't // require arcane bash and batch scripting, and lets you control both the output // *and* test the arguments, all without requiring writing any garbage files to // disk. // // PatchExecHelper *must* be embedded in your test suite in order to function. // It adds a test to your testsuite which by default simply does nothing. When // the patched exec.Command function is called (returned by GetExecCommand), // instead of running the requested executable, we call the test executable with // -check.f to rnu only TestExecSuiteHelperProcess, which acts as a configurable // main function. type PatchExecHelper struct{} // PatchExecConfig holds the arguments for PatchExecHelper.GetExecCommand. type PatchExecConfig struct { // Stderr is the value you'd like written to stderr. Stderr string // Stdout is the value you'd like written to stdout. Stdout string // ExitCode controls the exit code of the patched executable. ExitCode int // Args is a channel that will be sent the args passed to the patched // execCommand function. It should be a channel with a buffer equal to the // number of executions you expect to be run (often just 1). Do not use an // unbuffered channel unless you're reading the channel from another // goroutine, or you will almost certainly block your tests indefinitely. Args chan<- []string } // GetExecCommand returns a function that can be used to patch out a use of // exec.Command. See PatchExecConfig for details about the arguments. func (PatchExecHelper) GetExecCommand(cfg PatchExecConfig) func(string, ...string) *exec.Cmd { // This method doesn't technically need to be a method on PatchExecHelper, // but serves as a reminder to embed PatchExecHelper. return func(command string, args ...string) *exec.Cmd { // We redirect the command to call the test executable, telling it to // run the TestExecSuiteHelperProcess test that got embedded into the // test suite, and pass the original args at the end of our args. // // Note that we don't need to include the suite name in check.f, because // even if you have more than one suite embedding PatchExecHelper, all // the tests have the same imlpementation, and the first instance of the // test to run calls os.Exit, and therefore none of the other tests will // run. cs := []string{"-check.f=TestExecSuiteHelperProcess", "--", command} cs = append(cs, args...) cmd := exec.Command(os.Args[0], cs...) cmd.Env = append( // We must preserve os.Environ() on Windows, // or the subprocess will fail in weird and // wonderful ways. os.Environ(), "JUJU_WANT_HELPER_PROCESS=1", "JUJU_HELPER_PROCESS_STDERR="+cfg.Stderr, "JUJU_HELPER_PROCESS_STDOUT="+cfg.Stdout, fmt.Sprintf("JUJU_HELPER_PROCESS_EXITCODE=%d", cfg.ExitCode), ) // Pass the args back on the arg channel. This is why the channel needs // to be buffered, so this won't block. if cfg.Args != nil { cfg.Args <- append([]string{command}, args...) } return cmd } } // TestExecSuiteHelperProcess is a fake test which is added to your test suite // (because you remembered to embed PatchExecHelper in your suite, right?). It // allows us to use the test executable as a helper process to get expected // output for tests. When run normally during tests, this test simply does // nothing (and passes). The above patched exec.Command runs the test // executable with -check.f, it runs this test and enables the configurable // behavior. Because the test exits with os.Exit, no additional test output is // written. func (PatchExecHelper) TestExecSuiteHelperProcess(c *gc.C) { if os.Getenv("JUJU_WANT_HELPER_PROCESS") == "" { return } if stderr := os.Getenv("JUJU_HELPER_PROCESS_STDERR"); stderr != "" { fmt.Fprintln(os.Stderr, stderr) } if stdout := os.Getenv("JUJU_HELPER_PROCESS_STDOUT"); stdout != "" { fmt.Fprintln(os.Stdout, stdout) } code := os.Getenv("JUJU_HELPER_PROCESS_EXITCODE") if code == "" { os.Exit(0) } exit, err := strconv.Atoi(code) if err != nil { // This should be impossible, since we set this with an int above. panic(err) } os.Exit(exit) } // CaptureOutput runs the given function and captures anything written // to Stderr or Stdout during f's execution. func CaptureOutput(c *gc.C, f func()) (stdout []byte, stderr []byte) { dir := c.MkDir() stderrf, err := os.OpenFile(filepath.Join(dir, "stderr"), os.O_RDWR|os.O_CREATE, 0600) c.Assert(err, jc.ErrorIsNil) defer stderrf.Close() stdoutf, err := os.OpenFile(filepath.Join(dir, "stdout"), os.O_RDWR|os.O_CREATE, 0600) c.Assert(err, jc.ErrorIsNil) defer stdoutf.Close() // make a sub-functions so those defers go off ASAP. func() { origErr := os.Stderr defer func() { os.Stderr = origErr }() origOut := os.Stdout defer func() { os.Stdout = origOut }() os.Stderr = stderrf os.Stdout = stdoutf f() }() _, err = stderrf.Seek(0, 0) c.Assert(err, jc.ErrorIsNil) stderr, err = ioutil.ReadAll(stderrf) c.Assert(err, jc.ErrorIsNil) _, err = stdoutf.Seek(0, 0) c.Assert(err, jc.ErrorIsNil) stdout, err = ioutil.ReadAll(stdoutf) c.Assert(err, jc.ErrorIsNil) return stdout, stderr } testing-1.2.0/cmd_test.go000066400000000000000000000101641456110213600153210ustar00rootroot00000000000000// Copyright 2012-2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing_test import ( "bytes" "fmt" "os" "os/exec" "strings" gc "gopkg.in/check.v1" "github.com/juju/testing" jc "github.com/juju/testing/checkers" ) type cmdSuite struct { testing.CleanupSuite } var _ = gc.Suite(&cmdSuite{}) func (s *cmdSuite) TestHookCommandOutput(c *gc.C) { var CommandOutput = (*exec.Cmd).CombinedOutput cmdChan, cleanup := testing.HookCommandOutput(&CommandOutput, []byte{1, 2, 3, 4}, nil) defer cleanup() testCmd := exec.Command("fake-command", "arg1", "arg2") out, err := CommandOutput(testCmd) c.Assert(err, gc.IsNil) cmd := <-cmdChan c.Assert(out, gc.DeepEquals, []byte{1, 2, 3, 4}) c.Assert(cmd.Args, gc.DeepEquals, []string{"fake-command", "arg1", "arg2"}) } func (s *cmdSuite) EnsureArgFileRemoved(name string) { s.AddCleanup(func(c *gc.C) { c.Assert(name+".out", jc.DoesNotExist) }) } const testFunc = "test-output" func (s *cmdSuite) TestPatchExecutableNoArgs(c *gc.C) { s.EnsureArgFileRemoved(testFunc) testing.PatchExecutableAsEchoArgs(c, s, testFunc) output := runCommand(c, testFunc) output = strings.TrimRight(output, "\r\n") c.Assert(output, gc.Equals, testFunc) testing.AssertEchoArgs(c, testFunc) } func (s *cmdSuite) TestPatchExecutableWithArgs(c *gc.C) { s.EnsureArgFileRemoved(testFunc) testing.PatchExecutableAsEchoArgs(c, s, testFunc) output := runCommand(c, testFunc, "foo", "bar baz") output = strings.TrimRight(output, "\r\n") c.Assert(output, gc.DeepEquals, testFunc+" 'foo' 'bar baz'") testing.AssertEchoArgs(c, testFunc, "foo", "bar baz") } func (s *cmdSuite) TestPatchExecutableThrowError(c *gc.C) { testing.PatchExecutableThrowError(c, s, testFunc, 1) cmd := exec.Command(testFunc) out, err := cmd.CombinedOutput() c.Assert(err, gc.ErrorMatches, "exit status 1") output := strings.TrimRight(string(out), "\r\n") c.Assert(output, gc.Equals, "failing") } func (s *cmdSuite) TestCaptureOutput(c *gc.C) { f := func() { _, err := fmt.Fprint(os.Stderr, "this is stderr") c.Assert(err, jc.ErrorIsNil) _, err = fmt.Fprint(os.Stdout, "this is stdout") c.Assert(err, jc.ErrorIsNil) } stdout, stderr := testing.CaptureOutput(c, f) c.Check(string(stdout), gc.Equals, "this is stdout") c.Check(string(stderr), gc.Equals, "this is stderr") } var _ = gc.Suite(&ExecHelperSuite{}) type ExecHelperSuite struct { testing.PatchExecHelper } func (s *ExecHelperSuite) TestExecHelperError(c *gc.C) { argChan := make(chan []string, 1) cfg := testing.PatchExecConfig{ Stdout: "Hellooooo stdout!", Stderr: "Hellooooo stderr!", ExitCode: 55, Args: argChan, } f := s.GetExecCommand(cfg) stderr := &bytes.Buffer{} stdout := &bytes.Buffer{} cmd := f("echo", "hello world!") cmd.Stderr = stderr cmd.Stdout = stdout err := cmd.Run() c.Assert(err, gc.NotNil) _, ok := err.(*exec.ExitError) if !ok { c.Errorf("Expected *exec.ExitError, but got %T", err) } else { c.Check(err.Error(), gc.Equals, "exit status 55") } c.Check(stderr.String(), gc.Equals, cfg.Stderr+"\n") c.Check(stdout.String(), gc.Equals, cfg.Stdout+"\n") select { case args := <-argChan: c.Assert(args, gc.DeepEquals, []string{"echo", "hello world!"}) default: c.Fatalf("No arguments passed to output channel") } } func (s *ExecHelperSuite) TestExecHelper(c *gc.C) { argChan := make(chan []string, 1) cfg := testing.PatchExecConfig{ Stdout: "Hellooooo stdout!", Stderr: "Hellooooo stderr!", Args: argChan, } f := s.GetExecCommand(cfg) stderr := &bytes.Buffer{} stdout := &bytes.Buffer{} cmd := f("echo", "hello world!") cmd.Stderr = stderr cmd.Stdout = stdout err := cmd.Run() c.Assert(err, jc.ErrorIsNil) c.Check(stderr.String(), gc.Equals, cfg.Stderr+"\n") c.Check(stdout.String(), gc.Equals, cfg.Stdout+"\n") select { case args := <-argChan: c.Assert(args, gc.DeepEquals, []string{"echo", "hello world!"}) default: c.Fatalf("No arguments passed to output channel") } } func runCommand(c *gc.C, command string, args ...string) string { cmd := exec.Command(command, args...) out, err := cmd.CombinedOutput() c.Assert(err, jc.ErrorIsNil) return string(out) } testing-1.2.0/constants.go000066400000000000000000000013151456110213600155310ustar00rootroot00000000000000// Copyright 2018 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing import ( "time" ) // ShortWait is a reasonable amount of time to block waiting for something that // shouldn't actually happen. (as in, the test suite will *actually* wait this // long before continuing) const ShortWait = 50 * time.Millisecond // LongWait is used when something should have already happened, or happens // quickly, but we want to make sure we just haven't missed it. As in, the test // suite should proceed without sleeping at all, but just in case. It is long // so that we don't have spurious failures without actually slowing down the // test suite const LongWait = 10 * time.Second testing-1.2.0/filetesting/000077500000000000000000000000001456110213600155035ustar00rootroot00000000000000testing-1.2.0/filetesting/filetesting.go000066400000000000000000000125011456110213600203460ustar00rootroot00000000000000// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package filetesting import ( "io/ioutil" "os" "path/filepath" "runtime" jc "github.com/juju/testing/checkers" gc "gopkg.in/check.v1" ) // Entry represents a filesystem entity that can be created; and whose // correctness can be verified. type Entry interface { // GetPath returns the slash-separated relative path that this // entry represents. GetPath() string // Create causes the entry to be created, relative to basePath. It returns // a copy of the receiver. Create(c *gc.C, basePath string) Entry // Check checks that the entry exists, relative to basePath, and matches // the entry that would be created by Create. It returns a copy of the // receiver. Check(c *gc.C, basePath string) Entry } var ( _ Entry = Dir{} _ Entry = File{} _ Entry = Symlink{} _ Entry = Removed{} ) // Entries supplies convenience methods on Entry slices. type Entries []Entry // Paths returns the slash-separated path of every entry. func (e Entries) Paths() []string { result := make([]string, len(e)) for i, entry := range e { result[i] = entry.GetPath() } return result } // Create creates every entry relative to basePath and returns a copy of itself. func (e Entries) Create(c *gc.C, basePath string) Entries { result := make([]Entry, len(e)) for i, entry := range e { result[i] = entry.Create(c, basePath) } return result } // Check checks every entry relative to basePath and returns a copy of itself. func (e Entries) Check(c *gc.C, basePath string) Entries { result := make([]Entry, len(e)) for i, entry := range e { result[i] = entry.Check(c, basePath) } return result } // AsRemoveds returns a slice of Removed entries whose paths correspond to // those in e. func (e Entries) AsRemoveds() Entries { result := make([]Entry, len(e)) for i, entry := range e { result[i] = Removed{entry.GetPath()} } return result } // join joins a slash-separated path to a filesystem basePath. func join(basePath, path string) string { return filepath.Join(basePath, filepath.FromSlash(path)) } // Dir is an Entry that allows directories to be created and verified. The // Path field should use "/" as the path separator. type Dir struct { Path string Perm os.FileMode } func (d Dir) GetPath() string { return d.Path } func (d Dir) Create(c *gc.C, basePath string) Entry { path := join(basePath, d.Path) err := os.MkdirAll(path, d.Perm) c.Assert(err, gc.IsNil) err = os.Chmod(path, d.Perm) c.Assert(err, gc.IsNil) return d } func (d Dir) Check(c *gc.C, basePath string) Entry { path := join(basePath, d.Path) fileInfo, err := os.Lstat(path) comment := gc.Commentf("dir %q", path) if !c.Check(err, gc.IsNil, comment) { return d } // Skip until we implement proper permissions checking if runtime.GOOS != "windows" { c.Check(fileInfo.Mode()&os.ModePerm, gc.Equals, d.Perm, comment) } c.Check(fileInfo.Mode()&os.ModeType, gc.Equals, os.ModeDir, comment) return d } // File is an Entry that allows plain files to be created and verified. The // Path field should use "/" as the path separator. type File struct { Path string Data string Perm os.FileMode } func (f File) GetPath() string { return f.Path } func (f File) Create(c *gc.C, basePath string) Entry { path := join(basePath, f.Path) err := ioutil.WriteFile(path, []byte(f.Data), f.Perm) c.Assert(err, gc.IsNil) err = os.Chmod(path, f.Perm) c.Assert(err, gc.IsNil) return f } func (f File) Check(c *gc.C, basePath string) Entry { path := join(basePath, f.Path) fileInfo, err := os.Lstat(path) comment := gc.Commentf("file %q", path) if !c.Check(err, gc.IsNil, comment) { return f } // Skip until we implement proper permissions checking if runtime.GOOS != "windows" { mode := fileInfo.Mode() c.Check(mode&os.ModeType, gc.Equals, os.FileMode(0), comment) c.Check(mode&os.ModePerm, gc.Equals, f.Perm, comment) } data, err := ioutil.ReadFile(path) c.Check(err, gc.IsNil, comment) c.Check(string(data), gc.Equals, f.Data, comment) return f } // Symlink is an Entry that allows symlinks to be created and verified. The // Path field should use "/" as the path separator. type Symlink struct { Path string Link string } func (s Symlink) GetPath() string { return s.Path } func (s Symlink) Create(c *gc.C, basePath string) Entry { err := os.Symlink(s.Link, join(basePath, s.Path)) c.Assert(err, gc.IsNil) return s } func (s Symlink) Check(c *gc.C, basePath string) Entry { path := join(basePath, s.Path) comment := gc.Commentf("symlink %q", path) link, err := os.Readlink(path) c.Check(err, gc.IsNil, comment) c.Check(link, gc.Equals, s.Link, comment) return s } // Removed is an Entry that indicates the absence of any entry. The Path // field should use "/" as the path separator. type Removed struct { Path string } func (r Removed) GetPath() string { return r.Path } func (r Removed) Create(c *gc.C, basePath string) Entry { err := os.RemoveAll(join(basePath, r.Path)) c.Assert(err, gc.IsNil) return r } func (r Removed) Check(c *gc.C, basePath string) Entry { path := join(basePath, r.Path) _, err := os.Lstat(path) // isNotExist allows us to handle the following case: // File{"foo", ...}.Create(...) // Removed{"foo/bar"}.Check(...) // ...where os.IsNotExist would not work. c.Check(err, jc.Satisfies, isNotExist, gc.Commentf("removed %q", path)) return r } testing-1.2.0/filetesting/filetesting_test.go000066400000000000000000000215051456110213600214110ustar00rootroot00000000000000// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package filetesting_test import ( "io/ioutil" "os" "path/filepath" jc "github.com/juju/testing/checkers" gc "gopkg.in/check.v1" ft "github.com/juju/testing/filetesting" ) type EntrySuite struct { basePath string } var _ = gc.Suite(&EntrySuite{}) func (s *EntrySuite) SetUpTest(c *gc.C) { s.basePath = c.MkDir() } func (s *EntrySuite) join(path string) string { return filepath.Join(s.basePath, filepath.FromSlash(path)) } func (s *EntrySuite) TestFileCreate(c *gc.C) { ft.File{"foobar", "hello", 0666}.Create(c, s.basePath) path := s.join("foobar") info, err := os.Lstat(path) c.Assert(err, gc.IsNil) c.Assert(info.Mode()&os.ModePerm, gc.Equals, os.FileMode(0666)) c.Assert(info.Mode()&os.ModeType, gc.Equals, os.FileMode(0)) data, err := ioutil.ReadFile(path) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "hello") } func (s *EntrySuite) TestFileCreateFailure(c *gc.C) { c.ExpectFailure("should fail to create file in missing dir") ft.File{"missing/foobar", "hello", 0644}.Create(c, s.basePath) } func (s *EntrySuite) TestFileCheckSuccess(c *gc.C) { ft.File{"furble", "pingle", 0740}.Create(c, s.basePath) ft.File{"furble", "pingle", 0740}.Check(c, s.basePath) } func (s *EntrySuite) TestFileCheckFailureBadPerm(c *gc.C) { ft.File{"furble", "pingle", 0644}.Create(c, s.basePath) c.ExpectFailure("shouldn't pass with different perms") ft.File{"furble", "pingle", 0740}.Check(c, s.basePath) } func (s *EntrySuite) TestFileCheckFailureBadData(c *gc.C) { ft.File{"furble", "pingle", 0740}.Check(c, s.basePath) c.ExpectFailure("shouldn't pass with different content") ft.File{"furble", "wrongle", 0740}.Check(c, s.basePath) } func (s *EntrySuite) TestFileCheckFailureNoExist(c *gc.C) { c.ExpectFailure("shouldn't find file that does not exist") ft.File{"furble", "pingle", 0740}.Check(c, s.basePath) } func (s *EntrySuite) TestFileCheckFailureSymlink(c *gc.C) { ft.Symlink{"link", "file"}.Create(c, s.basePath) ft.File{"file", "content", 0644}.Create(c, s.basePath) c.ExpectFailure("shouldn't accept symlink, even if pointing to matching file") ft.File{"link", "content", 0644}.Check(c, s.basePath) } func (s *EntrySuite) TestFileCheckFailureDir(c *gc.C) { ft.Dir{"furble", 0740}.Create(c, s.basePath) c.ExpectFailure("shouldn't accept dir") ft.File{"furble", "pingle", 0740}.Check(c, s.basePath) } func (s *EntrySuite) TestDirCreate(c *gc.C) { ft.Dir{"path", 0750}.Create(c, s.basePath) info, err := os.Lstat(s.join("path")) c.Check(err, gc.IsNil) c.Check(info.Mode()&os.ModePerm, gc.Equals, os.FileMode(0750)) c.Check(info.Mode()&os.ModeType, gc.Equals, os.ModeDir) } func (s *EntrySuite) TestDirCreateChmod(c *gc.C) { ft.Dir{"name", 0750}.Create(c, s.basePath) expect := ft.Dir{"name", 0755}.Create(c, s.basePath) expect.Check(c, s.basePath) } func (s *EntrySuite) TestDirCreateSubdir(c *gc.C) { subdir := ft.Dir{"some/path", 0750}.Create(c, s.basePath) subdir.Check(c, s.basePath) ft.Dir{"some", 0750}.Check(c, s.basePath) } func (s *EntrySuite) TestDirCreateFailure(c *gc.C) { os.Chmod(s.basePath, 0444) defer os.Chmod(s.basePath, 0777) c.ExpectFailure("should fail to create file") ft.Dir{"foobar", 0750}.Create(c, s.basePath) } func (s *EntrySuite) TestDirCheck(c *gc.C) { ft.Dir{"fooble", 0751}.Create(c, s.basePath) ft.Dir{"fooble", 0751}.Check(c, s.basePath) } func (s *EntrySuite) TestDirCheckFailureNoExist(c *gc.C) { c.ExpectFailure("shouldn't find dir that does not exist") ft.Dir{"fooble", 0751}.Check(c, s.basePath) } func (s *EntrySuite) TestDirCheckFailureBadPerm(c *gc.C) { ft.Dir{"furble", 0740}.Check(c, s.basePath) c.ExpectFailure("shouldn't pass with different perms") ft.Dir{"furble", 0755}.Check(c, s.basePath) } func (s *EntrySuite) TestDirCheckFailureSymlink(c *gc.C) { ft.Symlink{"link", "dir"}.Create(c, s.basePath) ft.Dir{"dir", 0644}.Create(c, s.basePath) c.ExpectFailure("shouldn't accept symlink, even if pointing to matching dir") ft.Dir{"link", 0644}.Check(c, s.basePath) } func (s *EntrySuite) TestDirCheckFailureFile(c *gc.C) { ft.File{"blah", "content", 0644}.Create(c, s.basePath) c.ExpectFailure("shouldn't accept file") ft.Dir{"blah", 0644}.Check(c, s.basePath) } func (s *EntrySuite) TestSymlinkCreate(c *gc.C) { ft.Symlink{"link", "target"}.Create(c, s.basePath) target, err := os.Readlink(s.join("link")) c.Assert(err, gc.IsNil) c.Assert(target, gc.Equals, "target") } func (s *EntrySuite) TestSymlinkCreateFailure(c *gc.C) { c.ExpectFailure("should fail to create symlink in missing dir") ft.Symlink{"missing/link", "target"}.Create(c, s.basePath) } func (s *EntrySuite) TestSymlinkCheck(c *gc.C) { ft.Symlink{"link", "target"}.Create(c, s.basePath) ft.Symlink{"link", "target"}.Check(c, s.basePath) } func (s *EntrySuite) TestSymlinkCheckFailureNoExist(c *gc.C) { c.ExpectFailure("should not accept symlink that doesn't exist") ft.Symlink{"link", "target"}.Check(c, s.basePath) } func (s *EntrySuite) TestSymlinkCheckFailureBadTarget(c *gc.C) { ft.Symlink{"link", "target"}.Create(c, s.basePath) c.ExpectFailure("should not accept different target") ft.Symlink{"link", "different"}.Check(c, s.basePath) } func (s *EntrySuite) TestSymlinkCheckFailureFile(c *gc.C) { ft.File{"link", "target", 0644}.Create(c, s.basePath) c.ExpectFailure("should not accept plain file") ft.Symlink{"link", "target"}.Check(c, s.basePath) } func (s *EntrySuite) TestSymlinkCheckFailureDir(c *gc.C) { ft.Dir{"link", 0755}.Create(c, s.basePath) c.ExpectFailure("should not accept dir") ft.Symlink{"link", "different"}.Check(c, s.basePath) } func (s *EntrySuite) TestRemovedCreate(c *gc.C) { ft.File{"some-file", "content", 0644}.Create(c, s.basePath) ft.Removed{"some-file"}.Create(c, s.basePath) _, err := os.Lstat(s.join("some-file")) c.Assert(err, jc.Satisfies, os.IsNotExist) } func (s *EntrySuite) TestRemovedCreateNothing(c *gc.C) { ft.Removed{"some-file"}.Create(c, s.basePath) _, err := os.Lstat(s.join("some-file")) c.Assert(err, jc.Satisfies, os.IsNotExist) } func (s *EntrySuite) TestRemovedCreateFailure(c *gc.C) { ft.File{"some-file", "content", 0644}.Create(c, s.basePath) os.Chmod(s.basePath, 0444) defer os.Chmod(s.basePath, 0777) c.ExpectFailure("should fail to remove file") ft.Removed{"some-file"}.Create(c, s.basePath) } func (s *EntrySuite) TestRemovedCheck(c *gc.C) { ft.Removed{"some-file"}.Check(c, s.basePath) } func (s *EntrySuite) TestRemovedCheckParentNotDir(c *gc.C) { ft.File{"some-dir", "lol-not-a-file", 0644}.Create(c, s.basePath) ft.Removed{"some-dir/some-file"}.Check(c, s.basePath) } func (s *EntrySuite) TestRemovedCheckFailureFile(c *gc.C) { ft.File{"some-file", "", 0644}.Create(c, s.basePath) c.ExpectFailure("should not accept file") ft.Removed{"some-file"}.Check(c, s.basePath) } func (s *EntrySuite) TestRemovedCheckFailureDir(c *gc.C) { ft.Dir{"some-dir", 0755}.Create(c, s.basePath) c.ExpectFailure("should not accept dir") ft.Removed{"some-dir"}.Check(c, s.basePath) } func (s *EntrySuite) TestRemovedCheckFailureSymlink(c *gc.C) { ft.Symlink{"some-link", "target"}.Create(c, s.basePath) c.ExpectFailure("should not accept symlink") ft.Removed{"some-link"}.Check(c, s.basePath) } func (s *EntrySuite) TestCreateCheckChainResults(c *gc.C) { for i, test := range (ft.Entries{ ft.File{"some-file", "content", 0644}, ft.Dir{"some-dir", 0750}, ft.Symlink{"some-link", "target"}, ft.Removed{"missing"}, }) { c.Logf("test %d: %#v", i, test) chained := test.Create(c, s.basePath) chained = chained.Check(c, s.basePath) c.Assert(chained, jc.DeepEquals, test) } } func (s *EntrySuite) TestEntries(c *gc.C) { initial := ft.Entries{ ft.File{"some-file", "content", 0600}, ft.Dir{"some-dir", 0750}, ft.Symlink{"some-link", "target"}, ft.Removed{"missing"}, } expectRemoveds := ft.Entries{ ft.Removed{"some-file"}, ft.Removed{"some-dir"}, ft.Removed{"some-link"}, ft.Removed{"missing"}, } removeds := initial.AsRemoveds() c.Assert(removeds, jc.DeepEquals, expectRemoveds) expectPaths := []string{"some-file", "some-dir", "some-link", "missing"} c.Assert(initial.Paths(), jc.DeepEquals, expectPaths) c.Assert(removeds.Paths(), jc.DeepEquals, expectPaths) chainRemoveds := initial.Create(c, s.basePath).Check(c, s.basePath).AsRemoveds() c.Assert(chainRemoveds, jc.DeepEquals, expectRemoveds) chainRemoveds = chainRemoveds.Create(c, s.basePath).Check(c, s.basePath) c.Assert(chainRemoveds, jc.DeepEquals, expectRemoveds) } func (s *EntrySuite) TestEntriesCreateFailure(c *gc.C) { c.ExpectFailure("cannot create an entry") ft.Entries{ ft.File{"good", "good", 0750}, ft.File{"nodir/bad", "bad", 0640}, }.Create(c, s.basePath) } func (s *EntrySuite) TestEntriesCheckFailure(c *gc.C) { goodFile := ft.File{"good", "good", 0751}.Create(c, s.basePath) c.ExpectFailure("entry does not exist") ft.Entries{ goodFile, ft.File{"bad", "", 0750}, }.Check(c, s.basePath) } testing-1.2.0/filetesting/isnotexist_test.go000066400000000000000000000012561456110213600213060ustar00rootroot00000000000000// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package filetesting import ( "io/ioutil" "os" "path/filepath" jc "github.com/juju/testing/checkers" gc "gopkg.in/check.v1" ) type isNotExistSuite struct{} var _ = gc.Suite(&isNotExistSuite{}) func (*isNotExistSuite) TestIsNotExist(c *gc.C) { dir := c.MkDir() path := func(s string) string { return filepath.Join(dir, s) } err := ioutil.WriteFile(path("file"), []byte("blah"), 0644) c.Assert(err, gc.IsNil) _, err = os.Lstat(path("noexist")) c.Assert(err, jc.Satisfies, isNotExist) _, err = os.Lstat(path("file/parent-not-a-dir")) c.Assert(err, jc.Satisfies, isNotExist) } testing-1.2.0/filetesting/isnotexist_unix.go000066400000000000000000000015401456110213600213060ustar00rootroot00000000000000// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. // +build !windows package filetesting import ( "os" "syscall" ) // isNotExist returns true if the error is consistent with an attempt to // reference a file that does not exist. This works around the occasionally // unhelpful behaviour of os.IsNotExist, which does not recognise the error // produced when trying to read a path in which some component appears to // reference a directory but actually references a file. For example, if // "foo" is a file, an attempt to read "foo/bar" will generate an error that // does not satisfy os.IsNotExist, but will satisfy filetesting.isNotExist. func isNotExist(err error) bool { if os.IsNotExist(err) { return true } if e, ok := err.(*os.PathError); ok && e.Err == syscall.ENOTDIR { return true } return false } testing-1.2.0/filetesting/isnotexist_windows.go000066400000000000000000000004661456110213600220230ustar00rootroot00000000000000// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package filetesting import ( "os" ) // isNotExist returns true if the error is consistent with an attempt to // reference a file that does not exist. func isNotExist(err error) bool { return os.IsNotExist(err) } testing-1.2.0/filetesting/package_test.go000066400000000000000000000003321456110213600204620ustar00rootroot00000000000000// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package filetesting_test import ( "testing" gc "gopkg.in/check.v1" ) func Test(t *testing.T) { gc.TestingT(t) } testing-1.2.0/filetesting/stub.go000066400000000000000000000111121456110213600170030ustar00rootroot00000000000000// Copyright 2016 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package filetesting import ( "bytes" "hash" "io" "os" "strings" "time" "github.com/juju/errors" "github.com/juju/testing" ) type StubReader struct { Stub *testing.Stub ReturnRead io.Reader } func NewStubReader(stub *testing.Stub, content string) io.Reader { return &StubReader{ Stub: stub, ReturnRead: strings.NewReader(content), } } func (s *StubReader) Read(data []byte) (int, error) { s.Stub.AddCall("Read", data) if err := s.Stub.NextErr(); err != nil { return 0, errors.Trace(err) } if s.ReturnRead == nil { return 0, nil } return s.ReturnRead.Read(data) } type StubWriter struct { Stub *testing.Stub ReturnWrite io.Writer } func NewStubWriter(stub *testing.Stub) (io.Writer, *bytes.Buffer) { buf := new(bytes.Buffer) s := &StubWriter{ Stub: stub, ReturnWrite: buf, } return s, buf } func (s *StubWriter) Write(data []byte) (int, error) { s.Stub.AddCall("Write", data) if err := s.Stub.NextErr(); err != nil { return 0, errors.Trace(err) } if s.ReturnWrite == nil { return 0, nil } return s.ReturnWrite.Write(data) } type StubSeeker struct { Stub *testing.Stub ReturnSeek int64 } func (s *StubSeeker) Seek(offset int64, whence int) (int64, error) { s.Stub.AddCall("Seek", offset, whence) if err := s.Stub.NextErr(); err != nil { return 0, errors.Trace(err) } return s.ReturnSeek, nil } type StubCloser struct { Stub *testing.Stub } func (s *StubCloser) Close() error { s.Stub.AddCall("Close") if err := s.Stub.NextErr(); err != nil { return errors.Trace(err) } return nil } type StubFile struct { io.Reader io.Writer io.Seeker io.Closer Stub *testing.Stub Info StubFileInfo } func NewStubFile(stub *testing.Stub, raw io.ReadWriter) *StubFile { return &StubFile{ Reader: &StubReader{Stub: stub, ReturnRead: raw}, Writer: &StubWriter{Stub: stub, ReturnWrite: raw}, Seeker: &StubSeeker{Stub: stub}, Closer: &StubCloser{Stub: stub}, Stub: stub, } } func (s *StubFile) Name() string { s.Stub.AddCall("Name") s.Stub.NextErr() // Pop one off. return s.Info.Info.Name } func (s *StubFile) Stat() (os.FileInfo, error) { s.Stub.AddCall("Stat") if err := s.Stub.NextErr(); err != nil { return nil, errors.Trace(err) } return &s.Info, nil } func (s *StubFile) Sync() error { s.Stub.AddCall("Sync") if err := s.Stub.NextErr(); err != nil { return errors.Trace(err) } return nil } func (s *StubFile) Truncate(size int64) error { s.Stub.AddCall("Truncate", size) if err := s.Stub.NextErr(); err != nil { return errors.Trace(err) } return nil } type FileInfo struct { Name string Size int64 Mode os.FileMode ModTime time.Time } var _ os.FileInfo = (*StubFileInfo)(nil) type StubFileInfo struct { Stub *testing.Stub Info FileInfo ReturnSys interface{} } func NewStubFileInfo(stub *testing.Stub, name, content string) *StubFileInfo { return &StubFileInfo{ Stub: stub, Info: FileInfo{ Name: name, Size: int64(len(content)), Mode: 0644, ModTime: time.Now(), }, } } func (s StubFileInfo) Name() string { s.Stub.AddCall("Name") s.Stub.NextErr() // Pop one off. return s.Info.Name } func (s StubFileInfo) Size() int64 { s.Stub.AddCall("Size") s.Stub.NextErr() // Pop one off. return s.Info.Size } func (s StubFileInfo) Mode() os.FileMode { s.Stub.AddCall("Mode") s.Stub.NextErr() // Pop one off. return s.Info.Mode } func (s StubFileInfo) ModTime() time.Time { s.Stub.AddCall("ModTime") s.Stub.NextErr() // Pop one off. return s.Info.ModTime } func (s StubFileInfo) IsDir() bool { s.Stub.AddCall("IsDir") s.Stub.NextErr() // Pop one off. return s.Info.Mode.IsDir() } func (s StubFileInfo) Sys() interface{} { s.Stub.AddCall("Sys") s.Stub.NextErr() // Pop one off. return s.ReturnSys } var _ hash.Hash = (*StubHash)(nil) type StubHash struct { io.Writer Stub *testing.Stub ReturnSum []byte ReturnSize int ReturnBlockSize int } func NewStubHash(stub *testing.Stub, raw io.Writer) *StubHash { return &StubHash{ Writer: &StubWriter{Stub: stub, ReturnWrite: raw}, Stub: stub, } } func (s *StubHash) Sum(b []byte) []byte { s.Stub.AddCall("Sum", b) s.Stub.NextErr() // Pop one off. return s.ReturnSum } func (s *StubHash) Reset() { s.Stub.AddCall("Reset") s.Stub.NextErr() // Pop one off. } func (s *StubHash) Size() int { s.Stub.AddCall("Size") s.Stub.NextErr() // Pop one off. return s.ReturnSize } func (s *StubHash) BlockSize() int { s.Stub.AddCall("BlockSize") s.Stub.NextErr() // Pop one off. return s.ReturnBlockSize } testing-1.2.0/go.mod000066400000000000000000000010031456110213600142660ustar00rootroot00000000000000module github.com/juju/testing go 1.21 toolchain go1.21.6 require ( github.com/juju/errors v1.0.0 github.com/juju/loggo/v2 v2.0.0 github.com/juju/utils/v4 v4.0.0 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/yaml.v2 v2.4.0 ) require ( github.com/juju/clock v1.0.3 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect golang.org/x/crypto v0.19.0 // indirect golang.org/x/net v0.21.0 // indirect ) testing-1.2.0/go.sum000066400000000000000000000047441456110213600143320ustar00rootroot00000000000000github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/juju/clock v1.0.3 h1:yJHIsWXeU8j3QcBdiess09SzfiXRRrsjKPn2whnMeds= github.com/juju/clock v1.0.3/go.mod h1:HIBvJ8kiV/n7UHwKuCkdYL4l/MDECztHR2sAvWDxxf0= github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM= github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5Qe8= github.com/juju/loggo v1.0.0 h1:Y6ZMQOGR9Aj3BGkiWx7HBbIx6zNwNkxhVNOHU2i1bl0= github.com/juju/loggo v1.0.0/go.mod h1:NIXFioti1SmKAlKNuUwbMenNdef59IF52+ZzuOmHYkg= github.com/juju/loggo/v2 v2.0.0 h1:PzyVIn+NgoZ22QUtPgKF/lh+6SnaCOEXhcP+sE4FhOk= github.com/juju/loggo/v2 v2.0.0/go.mod h1:647d6WvXBLj5lvka2qBvccr7vMIvF2KFkEH+0ZuFOUM= github.com/juju/utils/v4 v4.0.0 h1:H4xMv3i8Rm33yd8V+7Vle1UkutvaJr0Tlir+asaExhs= github.com/juju/utils/v4 v4.0.0/go.mod h1:j5wVHbRzw2LF85mb3H46cPPXBkyw5k4laDL6cOW55LY= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= testing-1.2.0/goversion12.go000066400000000000000000000002651456110213600156760ustar00rootroot00000000000000// Copyright 2015 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. // +build go1.2 // +build !go1.3,!go1.4,!go1.5 package testing const GOVERSION = 1.2 testing-1.2.0/goversion13.go000066400000000000000000000002561456110213600156770ustar00rootroot00000000000000// Copyright 2015 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. // +build go1.3 // +build !go1.4,!go1.5 package testing const GOVERSION = 1.3 testing-1.2.0/goversion14.go000066400000000000000000000002471456110213600157000ustar00rootroot00000000000000// Copyright 2015 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. // +build go1.4 // +build !go1.5 package testing const GOVERSION = 1.4 testing-1.2.0/goversion15.go000066400000000000000000000002261456110213600156760ustar00rootroot00000000000000// Copyright 2015 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. // +build go1.5 package testing const GOVERSION = 1.5 testing-1.2.0/home.go000066400000000000000000000062451456110213600144540ustar00rootroot00000000000000// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing import ( "io/ioutil" "os" "path/filepath" "github.com/juju/utils/v4" gc "gopkg.in/check.v1" jc "github.com/juju/testing/checkers" ) type TestFile struct { Name, Data string } // FakeHome stores information about the user's home // environment so it can be cast aside for tests and // restored afterwards. type FakeHome struct { files []TestFile } func MakeFakeHome(c *gc.C) *FakeHome { fakeHome := c.MkDir() err := utils.SetHome(fakeHome) c.Assert(err, jc.ErrorIsNil) sshPath := filepath.Join(fakeHome, ".ssh") err = os.Mkdir(sshPath, 0777) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(filepath.Join(sshPath, "id_rsa"), []byte("private auth key\n"), 0600) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(filepath.Join(sshPath, "id_rsa.pub"), []byte("public auth key\n"), 0666) c.Assert(err, gc.IsNil) return &FakeHome{ files: []TestFile{}, } } func (h *FakeHome) AddFiles(c *gc.C, files ...TestFile) { for _, f := range files { path := filepath.Join(utils.Home(), f.Name) err := os.MkdirAll(filepath.Dir(path), 0700) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(path, []byte(f.Data), 0666) c.Assert(err, gc.IsNil) h.files = append(h.files, f) } } // FileContents returns the test file contents for the // given specified path (which may be relative, so // we compare with the base filename only). func (h *FakeHome) FileContents(c *gc.C, path string) string { for _, f := range h.files { if filepath.Base(f.Name) == filepath.Base(path) { return f.Data } } c.Fatalf("path attribute holds unknown test file: %q", path) panic("unreachable") } // FileExists returns if the given relative file path exists // in the fake home. func (h *FakeHome) FileExists(path string) bool { for _, f := range h.files { if f.Name == path { return true } } return false } // HomePath joins the specified path snippets and returns // an absolute path under Juju home. func HomePath(names ...string) string { all := append([]string{utils.Home()}, names...) return filepath.Join(all...) } // JujuXDGDataHomePath returns the test home path, it is just a convenience // for tests, if extra path snippets are passed they will be // joined to juju home. // This tool assumes ~/.config/juju as the juju home. func JujuXDGDataHomePath(names ...string) string { all := append([]string{".local", "share", "juju"}, names...) return HomePath(all...) } // FakeHomeSuite sets up a fake home directory before running tests. type FakeHomeSuite struct { CleanupSuite LoggingSuite Home *FakeHome } func (s *FakeHomeSuite) SetUpSuite(c *gc.C) { s.CleanupSuite.SetUpSuite(c) s.LoggingSuite.SetUpSuite(c) } func (s *FakeHomeSuite) TearDownSuite(c *gc.C) { s.LoggingSuite.TearDownSuite(c) s.CleanupSuite.TearDownSuite(c) } func (s *FakeHomeSuite) SetUpTest(c *gc.C) { s.CleanupSuite.SetUpTest(c) s.LoggingSuite.SetUpTest(c) home := utils.Home() s.Home = MakeFakeHome(c) s.AddCleanup(func(*gc.C) { err := utils.SetHome(home) c.Assert(err, jc.ErrorIsNil) }) } func (s *FakeHomeSuite) TearDownTest(c *gc.C) { s.LoggingSuite.TearDownTest(c) s.CleanupSuite.TearDownTest(c) } testing-1.2.0/home_test.go000066400000000000000000000053671456110213600155170ustar00rootroot00000000000000// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing_test import ( "io/ioutil" "path/filepath" "runtime" "github.com/juju/utils/v4" gc "gopkg.in/check.v1" "github.com/juju/testing" jc "github.com/juju/testing/checkers" ) type fakeHomeSuite struct { testing.IsolationSuite fakeHomeSuite testing.FakeHomeSuite } var _ = gc.Suite(&fakeHomeSuite{}) func (s *fakeHomeSuite) SetUpSuite(c *gc.C) { s.IsolationSuite.SetUpSuite(c) s.fakeHomeSuite = testing.FakeHomeSuite{} s.fakeHomeSuite.SetUpSuite(c) } func (s *fakeHomeSuite) SetUpTest(c *gc.C) { s.IsolationSuite.SetUpTest(c) err := utils.SetHome("/tmp/tests") c.Assert(err, jc.ErrorIsNil) } func (s *fakeHomeSuite) TearDownSuite(c *gc.C) { s.fakeHomeSuite.TearDownSuite(c) s.IsolationSuite.TearDownSuite(c) } func (s *fakeHomeSuite) TestHomeCreated(c *gc.C) { // A fake home is created and set. s.fakeHomeSuite.SetUpTest(c) home := utils.Home() c.Assert(home, gc.Not(gc.Equals), "/tmp/tests") c.Assert(home, jc.IsDirectory) s.fakeHomeSuite.TearDownTest(c) // The original home has been restored. switch runtime.GOOS { case "windows": c.Assert(utils.Home(), jc.SamePath, "C:/tmp/tests") default: c.Assert(utils.Home(), jc.SamePath, "/tmp/tests") } } func (s *fakeHomeSuite) TestSshDirSetUp(c *gc.C) { // The SSH directory is properly created and set up. s.fakeHomeSuite.SetUpTest(c) sshDir := testing.HomePath(".ssh") c.Assert(sshDir, jc.IsDirectory) PrivKeyFile := filepath.Join(sshDir, "id_rsa") c.Assert(PrivKeyFile, jc.IsNonEmptyFile) PubKeyFile := filepath.Join(sshDir, "id_rsa.pub") c.Assert(PubKeyFile, jc.IsNonEmptyFile) s.fakeHomeSuite.TearDownTest(c) } type makeFakeHomeSuite struct { testing.IsolationSuite home *testing.FakeHome } var _ = gc.Suite(&makeFakeHomeSuite{}) func (s *makeFakeHomeSuite) SetUpTest(c *gc.C) { s.IsolationSuite.SetUpTest(c) s.home = testing.MakeFakeHome(c) testFile := testing.TestFile{ Name: "testfile-name", Data: "testfile-data", } s.home.AddFiles(c, testFile) } func (s *makeFakeHomeSuite) TestAddFiles(c *gc.C) { // Files are correctly added to the fake home. expectedPath := filepath.Join(utils.Home(), "testfile-name") contents, err := ioutil.ReadFile(expectedPath) c.Assert(err, gc.IsNil) c.Assert(string(contents), gc.Equals, "testfile-data") } func (s *makeFakeHomeSuite) TestFileContents(c *gc.C) { // Files contents are returned as strings. contents := s.home.FileContents(c, "testfile-name") c.Assert(contents, gc.Equals, "testfile-data") } func (s *makeFakeHomeSuite) TestFileExists(c *gc.C) { // It is possible to check whether a file exists in the fake home. c.Assert(s.home.FileExists("testfile-name"), jc.IsTrue) c.Assert(s.home.FileExists("no-such-file"), jc.IsFalse) } testing-1.2.0/http.go000066400000000000000000000106431456110213600145000ustar00rootroot00000000000000// Copyright 2012, 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing import ( "bytes" "fmt" "io/ioutil" "net" "net/http" "os" "time" gc "gopkg.in/check.v1" ) type HTTPSuite struct{} var Server = NewHTTPServer(5 * time.Second) func (s *HTTPSuite) SetUpSuite(c *gc.C) { Server.Start() } func (s *HTTPSuite) TearDownSuite(c *gc.C) {} func (s *HTTPSuite) SetUpTest(c *gc.C) {} func (s *HTTPSuite) TearDownTest(c *gc.C) { Server.Flush() } func (s *HTTPSuite) URL(path string) string { return Server.URL + path } type HTTPServer struct { URL string Timeout time.Duration started bool request chan *http.Request response chan ResponseFunc } func NewHTTPServer(timeout time.Duration) *HTTPServer { return &HTTPServer{Timeout: timeout} } type Response struct { Status int Headers map[string]string Body []byte } type ResponseFunc func(path string) Response func (s *HTTPServer) Start() { if s.started { return } s.started = true s.request = make(chan *http.Request, 64) s.response = make(chan ResponseFunc, 64) l, err := net.Listen("tcp", "localhost:0") if err != nil { panic(err) } port := l.Addr().(*net.TCPAddr).Port s.URL = fmt.Sprintf("http://localhost:%d", port) go http.Serve(l, s) s.Response(203, nil, nil) for { // Wait for it to be up. resp, err := http.Get(s.URL) if err == nil && resp.StatusCode == 203 { break } time.Sleep(1e8) } s.WaitRequest() // Consume dummy request. } // Flush discards all pending requests and responses. func (s *HTTPServer) Flush() { for { select { case <-s.request: case <-s.response: default: return } } } func body(req *http.Request) string { data, err := ioutil.ReadAll(req.Body) if err != nil { panic(err) } return string(data) } func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { req.ParseMultipartForm(1e6) data, err := ioutil.ReadAll(req.Body) if err != nil { panic(err) } req.Body = ioutil.NopCloser(bytes.NewBuffer(data)) s.request <- req var resp Response select { case respFunc := <-s.response: resp = respFunc(req.URL.Path) case <-time.After(s.Timeout): const msg = "ERROR: Timeout waiting for test to prepare a response\n" fmt.Fprintf(os.Stderr, msg) resp = Response{500, nil, []byte(msg)} } if resp.Headers != nil { h := w.Header() for k, v := range resp.Headers { h.Set(k, v) } } if resp.Status != 0 { w.WriteHeader(resp.Status) } w.Write(resp.Body) } // WaitRequests returns the next n requests made to the http server from // the queue. If not enough requests were previously made, it waits until // the timeout value for them to be made. func (s *HTTPServer) WaitRequests(n int) []*http.Request { reqs := make([]*http.Request, 0, n) for i := 0; i < n; i++ { select { case req := <-s.request: reqs = append(reqs, req) case <-time.After(s.Timeout): panic("Timeout waiting for request") } } return reqs } // WaitRequest returns the next request made to the http server from // the queue. If no requests were previously made, it waits until the // timeout value for one to be made. func (s *HTTPServer) WaitRequest() *http.Request { return s.WaitRequests(1)[0] } // ResponseFunc prepares the test server to respond the following n // requests using f to build each response. func (s *HTTPServer) ResponseFunc(n int, f ResponseFunc) { for i := 0; i < n; i++ { s.response <- f } } // ResponseMap maps request paths to responses. type ResponseMap map[string]Response // ResponseMap prepares the test server to respond the following n // requests using the m to obtain the responses. func (s *HTTPServer) ResponseMap(n int, m ResponseMap) { f := func(path string) Response { for rpath, resp := range m { if rpath == path { return resp } } body := []byte("Path not found in response map: " + path) return Response{Status: 500, Body: body} } s.ResponseFunc(n, f) } // Responses prepares the test server to respond the following n requests // using the provided response parameters. func (s *HTTPServer) Responses(n int, status int, headers map[string]string, body []byte) { f := func(path string) Response { return Response{status, headers, body} } s.ResponseFunc(n, f) } // Response prepares the test server to respond the following request // using the provided response parameters. func (s *HTTPServer) Response(status int, headers map[string]string, body []byte) { s.Responses(1, status, headers, body) } testing-1.2.0/httptesting/000077500000000000000000000000001456110213600155435ustar00rootroot00000000000000testing-1.2.0/httptesting/http.go000066400000000000000000000265551456110213600170660ustar00rootroot00000000000000// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package httptesting import ( "bytes" "encoding/json" "io" "io/ioutil" "net/http" "net/http/httptest" "net/textproto" "net/url" "strings" gc "gopkg.in/check.v1" jc "github.com/juju/testing/checkers" ) // BodyAsserter represents a function that can assert the correctness of // a JSON reponse. type BodyAsserter func(c *gc.C, body json.RawMessage) // JSONCallParams holds parameters for AssertJSONCall. // If left empty, some fields will automatically be filled with defaults. type JSONCallParams struct { // Do is used to make the HTTP request. // If it is nil, http.DefaultClient.Do will be used. // If the body reader implements io.Seeker, // req.Body will also implement that interface. Do func(req *http.Request) (*http.Response, error) // ExpectError holds the error regexp to match // against the error returned from the HTTP Do // request. If it is empty, the error is expected to be // nil. ExpectError string // Method holds the HTTP method to use for the call. // GET is assumed if this is empty. Method string // URL holds the URL to pass when making the request. // If the URL does not contain a host, a temporary // HTTP server is started running the Handler below // which is used for the host. URL string // Handler holds the handler to use to make the request. // It is ignored if the above URL field has a host part. Handler http.Handler // JSONBody specifies a JSON value to marshal to use // as the body of the request. If this is specified, Body will // be ignored and the Content-Type header will // be set to application/json. The request // body will implement io.Seeker. JSONBody interface{} // Body holds the body to send in the request. Body io.Reader // Header specifies the HTTP headers to use when making // the request. Header http.Header // ContentLength specifies the length of the body. // It may be zero, in which case the default net/http // content-length behaviour will be used. ContentLength int64 // Username, if specified, is used for HTTP basic authentication. Username string // Password, if specified, is used for HTTP basic authentication. Password string // ExpectStatus holds the expected HTTP status code. // http.StatusOK is assumed if this is zero. ExpectStatus int // ExpectBody holds the expected JSON body. // This may be a function of type BodyAsserter in which case it // will be called with the http response body to check the // result. ExpectBody interface{} // ExpectHeader holds any HTTP headers that must be present in the response. // Note that the response may also contain headers not in this field. ExpectHeader http.Header // Cookies, if specified, are added to the request. Cookies []*http.Cookie } // AssertJSONCall asserts that when the given handler is called with // the given parameters, the result is as specified. func AssertJSONCall(c *gc.C, p JSONCallParams) { c.Logf("JSON call, url %q", p.URL) if p.ExpectStatus == 0 { p.ExpectStatus = http.StatusOK } rec := DoRequest(c, DoRequestParams{ Do: p.Do, ExpectError: p.ExpectError, Handler: p.Handler, Method: p.Method, URL: p.URL, Body: p.Body, JSONBody: p.JSONBody, Header: p.Header, ContentLength: p.ContentLength, Username: p.Username, Password: p.Password, Cookies: p.Cookies, }) if p.ExpectError != "" { return } AssertJSONResponse(c, rec, p.ExpectStatus, p.ExpectBody) for k, v := range p.ExpectHeader { c.Assert(rec.HeaderMap[textproto.CanonicalMIMEHeaderKey(k)], gc.DeepEquals, v, gc.Commentf("header %q", k)) } } // AssertJSONResponse asserts that the given response recorder has // recorded the given HTTP status, response body and content type. If // expectBody is of type BodyAsserter it will be called with the response // body to ensure the response is correct. func AssertJSONResponse(c *gc.C, rec *httptest.ResponseRecorder, expectStatus int, expectBody interface{}) { c.Assert(rec.Code, gc.Equals, expectStatus, gc.Commentf("body: %s", rec.Body.Bytes())) // Ensure the response includes the expected body. if expectBody == nil { c.Assert(rec.Body.Bytes(), gc.HasLen, 0) return } c.Assert(rec.Header().Get("Content-Type"), gc.Equals, "application/json") if assertBody, ok := expectBody.(BodyAsserter); ok { var data json.RawMessage err := json.Unmarshal(rec.Body.Bytes(), &data) c.Assert(err, jc.ErrorIsNil, gc.Commentf("body: %s", rec.Body.Bytes())) assertBody(c, data) return } c.Assert(rec.Body.String(), jc.JSONEquals, expectBody) } // DoRequestParams holds parameters for DoRequest. // If left empty, some fields will automatically be filled with defaults. type DoRequestParams struct { // Do is used to make the HTTP request. // If it is nil, http.DefaultClient.Do will be used. // If the body reader implements io.Seeker, // req.Body will also implement that interface. Do func(req *http.Request) (*http.Response, error) // ExpectError holds the error regexp to match // against the error returned from the HTTP Do // request. If it is empty, the error is expected to be // nil. ExpectError string // ExpectStatus holds the expected HTTP status code. // If unset or zero, then no check is performed. ExpectStatus int // Method holds the HTTP method to use for the call. // GET is assumed if this is empty. Method string // URL holds the URL to pass when making the request. // If the URL does not contain a host, a temporary // HTTP server is started running the Handler below // which is used for the host. URL string // Handler holds the handler to use to make the request. // It is ignored if the above URL field has a host part. Handler http.Handler // JSONBody specifies a JSON value to marshal to use // as the body of the request. If this is specified, Body will // be ignored and the Content-Type header will // be set to application/json. The request // body will implement io.Seeker. JSONBody interface{} // Body holds the body to send in the request. Body io.Reader // Header specifies the HTTP headers to use when making // the request. Header http.Header // ContentLength specifies the length of the body. // It may be zero, in which case the default net/http // content-length behaviour will be used. ContentLength int64 // Username, if specified, is used for HTTP basic authentication. Username string // Password, if specified, is used for HTTP basic authentication. Password string // Cookies, if specified, are added to the request. Cookies []*http.Cookie } // DoRequest is the same as Do except that it returns // an httptest.ResponseRecorder instead of an http.Response. // This function exists for backward compatibility reasons. func DoRequest(c *gc.C, p DoRequestParams) *httptest.ResponseRecorder { resp := Do(c, p) if p.ExpectError != "" { return nil } defer resp.Body.Close() rec := httptest.NewRecorder() h := rec.Header() for k, v := range resp.Header { h[k] = v } rec.WriteHeader(resp.StatusCode) _, err := io.Copy(rec.Body, resp.Body) c.Assert(err, jc.ErrorIsNil) return rec } // Do invokes a request on the given handler with the given // parameters and returns the resulting HTTP response. // Note that, as with http.Client.Do, the response body // must be closed. func Do(c *gc.C, p DoRequestParams) *http.Response { if p.Method == "" { p.Method = "GET" } if p.Do == nil { p.Do = http.DefaultClient.Do } if reqURL, err := url.Parse(p.URL); err == nil && reqURL.Host == "" { srv := httptest.NewServer(p.Handler) defer srv.Close() p.URL = srv.URL + p.URL } if p.JSONBody != nil { data, err := json.Marshal(p.JSONBody) c.Assert(err, jc.ErrorIsNil) p.Body = bytes.NewReader(data) } // Note: we avoid NewRequest's odious reader wrapping by using // a custom nopCloser function. req, err := http.NewRequest(p.Method, p.URL, nopCloser(p.Body)) c.Assert(err, jc.ErrorIsNil) if p.JSONBody != nil { req.Header.Set("Content-Type", "application/json") } for key, val := range p.Header { req.Header[key] = val } if p.ContentLength != 0 { req.ContentLength = p.ContentLength } else { req.ContentLength = bodyContentLength(p.Body) } if p.Username != "" || p.Password != "" { req.SetBasicAuth(p.Username, p.Password) } for _, cookie := range p.Cookies { req.AddCookie(cookie) } resp, err := p.Do(req) if p.ExpectError != "" { c.Assert(err, gc.ErrorMatches, p.ExpectError) return nil } // malformed error check here is required to ensure that we handle cases // where prior to go version 1.12 if you try and access HTTPS from a HTTP // end point you recieved garbage back. In go version 1.12 and higher, the // status code of 400 is returned. The issue with this is that we should // handle both go version <1.11 and go >=1.12 in the same way. Juju // shouldn't have to know about the idiosyncrasies of the go runtime. malformed := malformedError(err) if err != nil && !malformed { c.Assert(err, jc.ErrorIsNil) } if p.ExpectStatus != 0 { statusCode := http.StatusBadRequest if !malformed { statusCode = resp.StatusCode } c.Assert(statusCode, gc.Equals, p.ExpectStatus) } return resp } func malformedError(err error) bool { if err == nil { return false } return strings.Contains(err.Error(), "transport connection broken: malformed HTTP response") } // bodyContentLength returns the Content-Length // to use for the given body. Usually http.NewRequest // would infer this (and the cases here come directly // from the logic in that function) but unfortunately // there's no way to avoid the NopCloser wrapping // for any of the types mentioned here. func bodyContentLength(body io.Reader) int64 { n := 0 switch v := body.(type) { case *bytes.Buffer: n = v.Len() case *bytes.Reader: n = v.Len() case *strings.Reader: n = v.Len() } return int64(n) } // nopCloser is like ioutil.NopCloser except that // the returned value implements io.Seeker if // r implements io.Seeker func nopCloser(r io.Reader) io.ReadCloser { if r == nil { return nil } rc, ok := r.(io.ReadCloser) if ok { return rc } rs, ok := r.(io.ReadSeeker) if ok { return readSeekNopCloser{rs} } return ioutil.NopCloser(r) } type readSeekNopCloser struct { io.ReadSeeker } func (readSeekNopCloser) Close() error { return nil } // URLRewritingTransport is an http.RoundTripper that can rewrite request // URLs. If the request URL has the prefix specified in Match that part // will be changed to the value specified in Replace. RoundTripper will // then be used to perform the resulting request. If RoundTripper is nil // http.DefaultTransport will be used. // // This can be used in tests that, for whatever reason, need to make a // call to a URL that's not in our control but we want to control the // results of HTTP requests to that URL. type URLRewritingTransport struct { MatchPrefix string Replace string RoundTripper http.RoundTripper } // RoundTrip implements http.RoundTripper. func (t URLRewritingTransport) RoundTrip(req *http.Request) (*http.Response, error) { rt := t.RoundTripper if rt == nil { rt = http.DefaultTransport } if !strings.HasPrefix(req.URL.String(), t.MatchPrefix) { return rt.RoundTrip(req) } req1 := *req var err error req1.URL, err = url.Parse(t.Replace + strings.TrimPrefix(req.URL.String(), t.MatchPrefix)) if err != nil { panic(err) } resp, err := rt.RoundTrip(&req1) if resp != nil { resp.Request = req } return resp, err } testing-1.2.0/httptesting/http_test.go000066400000000000000000000224311456110213600201120ustar00rootroot00000000000000// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package httptesting_test import ( "bytes" "encoding/json" "fmt" "io" "io/ioutil" "net/http" "net/http/httptest" "strings" gc "gopkg.in/check.v1" jc "github.com/juju/testing/checkers" "github.com/juju/testing/httptesting" ) type requestsSuite struct{} var _ = gc.Suite(&requestsSuite{}) // handlerResponse holds the body of a testing handler response. type handlerResponse struct { URL string Method string Body string Auth bool Header http.Header } func makeHandler(c *gc.C, status int, ctype string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { body, err := ioutil.ReadAll(req.Body) c.Assert(err, jc.ErrorIsNil) hasAuth := req.Header.Get("Authorization") != "" for _, h := range []string{"User-Agent", "Content-Length", "Accept-Encoding", "Authorization"} { delete(req.Header, h) } // Create the response. response := handlerResponse{ URL: req.URL.String(), Method: req.Method, Body: string(body), Header: req.Header, Auth: hasAuth, } // Write the response. w.Header().Set("Content-Type", ctype) w.WriteHeader(status) enc := json.NewEncoder(w) err = enc.Encode(response) c.Assert(err, jc.ErrorIsNil) }) } var assertJSONCallTests = []struct { about string params httptesting.JSONCallParams }{{ about: "simple request", params: httptesting.JSONCallParams{ Method: "GET", URL: "/", }, }, { about: "method not specified", params: httptesting.JSONCallParams{ URL: "/", }, }, { about: "POST request with a body", params: httptesting.JSONCallParams{ Method: "POST", URL: "/my/url", Body: strings.NewReader("request body"), }, }, { about: "GET request with custom headers", params: httptesting.JSONCallParams{ Method: "GET", URL: "/my/url", Header: http.Header{ "Custom1": {"header1", "header2"}, "Custom2": {"foo"}, }, }, }, { about: "POST request with a JSON body", params: httptesting.JSONCallParams{ Method: "POST", URL: "/my/url", JSONBody: map[string]int{"hello": 99}, }, }, { about: "authentication", params: httptesting.JSONCallParams{ URL: "/", Method: "PUT", Username: "who", Password: "bad-wolf", ExpectStatus: http.StatusOK, }, }, { about: "test for ExceptHeader in response", params: httptesting.JSONCallParams{ URL: "/", Do: func(req *http.Request) (*http.Response, error) { resp, err := http.DefaultClient.Do(req) resp.StatusCode = http.StatusOK resp.Header["Custom"] = []string{"value1", "value2"} resp.Header["Ignored"] = []string{"value3", "value3"} return resp, err }, ExpectStatus: http.StatusOK, ExpectHeader: http.Header{ "Custom": {"value1", "value2"}, }, }, }, { about: "test case insensitive for ExceptHeader in response", params: httptesting.JSONCallParams{ URL: "/", Do: func(req *http.Request) (*http.Response, error) { resp, err := http.DefaultClient.Do(req) resp.StatusCode = http.StatusOK resp.Header["Custom"] = []string{"value1", "value2"} resp.Header["Ignored"] = []string{"value3", "value3"} return resp, err }, ExpectStatus: http.StatusOK, ExpectHeader: http.Header{ "CUSTOM": {"value1", "value2"}, }, }, }, { about: "error status", params: httptesting.JSONCallParams{ URL: "/", ExpectStatus: http.StatusBadRequest, }, }, { about: "custom Do", params: httptesting.JSONCallParams{ URL: "/", ExpectStatus: http.StatusTeapot, Do: func(req *http.Request) (*http.Response, error) { resp, err := http.DefaultClient.Do(req) resp.StatusCode = http.StatusTeapot return resp, err }, }, }, { about: "custom Do with seekable JSON body", params: httptesting.JSONCallParams{ URL: "/", ExpectStatus: http.StatusTeapot, JSONBody: 123, Do: func(req *http.Request) (*http.Response, error) { r, ok := req.Body.(io.ReadSeeker) if !ok { return nil, fmt.Errorf("body is not seeker") } data, err := ioutil.ReadAll(r) if err != nil { panic(err) } if string(data) != "123" { panic(fmt.Errorf(`unexpected body content, got %q want "123"`, data)) } r.Seek(0, 0) resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } resp.StatusCode = http.StatusTeapot return resp, err }, }, }, { about: "expect error", params: httptesting.JSONCallParams{ URL: "/", ExpectStatus: http.StatusTeapot, Do: func(req *http.Request) (*http.Response, error) { return nil, fmt.Errorf("some error") }, ExpectError: "some error", }, }, { about: "expect error regexp", params: httptesting.JSONCallParams{ URL: "/", ExpectStatus: http.StatusTeapot, Do: func(req *http.Request) (*http.Response, error) { return nil, fmt.Errorf("some bad error") }, ExpectError: "some .* error", }, }} func (*requestsSuite) TestAssertJSONCall(c *gc.C) { for i, test := range assertJSONCallTests { c.Logf("test %d: %s", i, test.about) params := test.params // A missing status is assumed to be http.StatusOK. status := params.ExpectStatus if status == 0 { status = http.StatusOK } // Create the HTTP handler for this test. params.Handler = makeHandler(c, status, "application/json") // Populate the expected body parameter. expectBody := handlerResponse{ URL: params.URL, Method: params.Method, Header: params.Header, } // A missing method is assumed to be "GET". if expectBody.Method == "" { expectBody.Method = "GET" } expectBody.Header = make(http.Header) if params.JSONBody != nil { expectBody.Header.Set("Content-Type", "application/json") } for k, v := range params.Header { expectBody.Header[k] = v } if params.JSONBody != nil { data, err := json.Marshal(params.JSONBody) c.Assert(err, jc.ErrorIsNil) expectBody.Body = string(data) params.Body = bytes.NewReader(data) } else if params.Body != nil { // Handle the request body parameter. body, err := ioutil.ReadAll(params.Body) c.Assert(err, jc.ErrorIsNil) expectBody.Body = string(body) params.Body = bytes.NewReader(body) } // Handle basic HTTP authentication. if params.Username != "" || params.Password != "" { expectBody.Auth = true } params.ExpectBody = expectBody httptesting.AssertJSONCall(c, params) } } func (*requestsSuite) TestAssertJSONCallWithBodyAsserter(c *gc.C) { called := false params := httptesting.JSONCallParams{ URL: "/", Handler: makeHandler(c, http.StatusOK, "application/json"), ExpectBody: httptesting.BodyAsserter(func(c1 *gc.C, body json.RawMessage) { c.Assert(c1, gc.Equals, c) c.Assert(string(body), jc.JSONEquals, handlerResponse{ URL: "/", Method: "GET", Header: make(http.Header), }) called = true }), } httptesting.AssertJSONCall(c, params) c.Assert(called, gc.Equals, true) } func (*requestsSuite) TestAssertJSONCallWithHostedURL(c *gc.C) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write([]byte(fmt.Sprintf("%q", "ok "+req.URL.Path))) })) defer srv.Close() httptesting.AssertJSONCall(c, httptesting.JSONCallParams{ URL: srv.URL + "/foo", ExpectBody: "ok /foo", }) } var bodyReaderFuncs = []func(string) io.Reader{ func(s string) io.Reader { return strings.NewReader(s) }, func(s string) io.Reader { return bytes.NewBufferString(s) }, func(s string) io.Reader { return bytes.NewReader([]byte(s)) }, } func (*requestsSuite) TestDoRequestWithInferrableContentLength(c *gc.C) { text := "hello, world" for i, f := range bodyReaderFuncs { c.Logf("test %d", i) called := false httptesting.DoRequest(c, httptesting.DoRequestParams{ Handler: http.HandlerFunc(func(_ http.ResponseWriter, req *http.Request) { c.Check(req.ContentLength, gc.Equals, int64(len(text))) called = true }), Body: f(text), }) c.Assert(called, gc.Equals, true) } } // The TestAssertJSONCall above exercises the testing.AssertJSONCall succeeding // calls. Failures are already massively tested in practice. DoRequest and // AssertJSONResponse are also indirectly tested as they are called by // AssertJSONCall. type urlRewritingTransportSuite struct { server *httptest.Server } var _ = gc.Suite(&urlRewritingTransportSuite{}) func (s *urlRewritingTransportSuite) SetUpTest(c *gc.C) { s.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(r.URL.String())) })) } func (s *urlRewritingTransportSuite) TestTransport(c *gc.C) { t := httptesting.URLRewritingTransport{ MatchPrefix: "http://example.com", Replace: s.server.URL, } client := http.Client{ Transport: &t, } resp, err := client.Get("http://example.com/path") c.Assert(err, jc.ErrorIsNil) body, err := ioutil.ReadAll(resp.Body) c.Assert(err, jc.ErrorIsNil) resp.Body.Close() c.Assert(resp.Request.URL.String(), gc.Equals, "http://example.com/path") c.Assert(string(body), gc.Equals, "/path") t.RoundTripper = &http.Transport{} resp, err = client.Get(s.server.URL + "/otherpath") c.Assert(err, jc.ErrorIsNil) body, err = ioutil.ReadAll(resp.Body) c.Assert(err, jc.ErrorIsNil) resp.Body.Close() c.Assert(resp.Request.URL.String(), gc.Equals, s.server.URL+"/otherpath") c.Assert(string(body), gc.Equals, "/otherpath") } testing-1.2.0/httptesting/package_test.go000066400000000000000000000003321456110213600205220ustar00rootroot00000000000000// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package httptesting_test import ( "testing" gc "gopkg.in/check.v1" ) func Test(t *testing.T) { gc.TestingT(t) } testing-1.2.0/imports.go000066400000000000000000000131041456110213600152110ustar00rootroot00000000000000// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing import ( "fmt" "go/build" "path/filepath" "sort" "strings" ) // stdlib repesents the packages that belong to the standard library. var stdlib = map[string]bool{ "C": true, // not really a package "archive/tar": true, "archive/zip": true, "bufio": true, "bytes": true, "compress/bzip2": true, "compress/flate": true, "compress/gzip": true, "compress/lzw": true, "compress/zlib": true, "container/heap": true, "container/list": true, "container/ring": true, "crypto": true, "crypto/aes": true, "crypto/cipher": true, "crypto/des": true, "crypto/dsa": true, "crypto/ecdsa": true, "crypto/elliptic": true, "crypto/hmac": true, "crypto/md5": true, "crypto/rand": true, "crypto/rc4": true, "crypto/rsa": true, "crypto/sha1": true, "crypto/sha256": true, "crypto/sha512": true, "crypto/subtle": true, "crypto/tls": true, "crypto/x509": true, "crypto/x509/pkix": true, "database/sql": true, "database/sql/driver": true, "debug/dwarf": true, "debug/elf": true, "debug/goobj": true, "debug/gosym": true, "debug/macho": true, "debug/pe": true, "debug/plan9obj": true, "encoding": true, "encoding/ascii85": true, "encoding/asn1": true, "encoding/base32": true, "encoding/base64": true, "encoding/binary": true, "encoding/csv": true, "encoding/gob": true, "encoding/hex": true, "encoding/json": true, "encoding/pem": true, "encoding/xml": true, "errors": true, "expvar": true, "flag": true, "fmt": true, "go/ast": true, "go/build": true, "go/doc": true, "go/format": true, "go/parser": true, "go/printer": true, "go/scanner": true, "go/token": true, "hash": true, "hash/adler32": true, "hash/crc32": true, "hash/crc64": true, "hash/fnv": true, "html": true, "html/template": true, "image": true, "image/color": true, "image/color/palette": true, "image/draw": true, "image/gif": true, "image/jpeg": true, "image/png": true, "index/suffixarray": true, "io": true, "io/ioutil": true, "log": true, "log/syslog": true, "math": true, "math/big": true, "math/cmplx": true, "math/rand": true, "mime": true, "mime/multipart": true, "net": true, "net/http": true, "net/http/cgi": true, "net/http/cookiejar": true, "net/http/fcgi": true, "net/http/httptest": true, "net/http/httputil": true, "net/http/pprof": true, "net/mail": true, "net/rpc": true, "net/rpc/jsonrpc": true, "net/smtp": true, "net/textproto": true, "net/url": true, "os": true, "os/exec": true, "os/signal": true, "os/user": true, "path": true, "path/filepath": true, "reflect": true, "regexp": true, "regexp/syntax": true, "runtime": true, "runtime/cgo": true, "runtime/debug": true, "runtime/pprof": true, "runtime/race": true, "sort": true, "strconv": true, "strings": true, "sync": true, "sync/atomic": true, "syscall": true, "testing": true, "testing/iotest": true, "testing/quick": true, "text/scanner": true, "text/tabwriter": true, "text/template": true, "text/template/parse": true, "time": true, "unicode": true, "unicode/utf16": true, "unicode/utf8": true, "unsafe": true, } // FindImports returns a sorted list of packages imported by the package // with the given name that have the given prefix. The resulting list // removes the common prefix, leaving just the short names. func FindImports(packageName, prefix string) ([]string, error) { allPkgs := make(map[string]bool) if err := findImports(packageName, allPkgs, findProjectDir(prefix)); err != nil { return nil, err } var result []string for name := range allPkgs { if strings.HasPrefix(name, prefix) { result = append(result, name[len(prefix):]) } } sort.Strings(result) return result, nil } func findProjectDir(projectPath string) string { return filepath.Join(build.Default.GOPATH, "src", projectPath) } // findImports recursively adds all imported packages of given // package (packageName) to allPkgs map. func findImports(packageName string, allPkgs map[string]bool, srcDir string) error { // skip packages defined in the standard library. if stdlib[packageName] { return nil } pkg, err := build.Default.Import(packageName, srcDir, 0) if err != nil { return fmt.Errorf("cannot find %q: %v", packageName, err) } for _, name := range pkg.Imports { if !allPkgs[name] { allPkgs[name] = true if err := findImports(name, allPkgs, srcDir); err != nil { return err } } } return nil } testing-1.2.0/imports_test.go000066400000000000000000000017111456110213600162510ustar00rootroot00000000000000// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing_test import ( gc "gopkg.in/check.v1" "github.com/juju/testing" jc "github.com/juju/testing/checkers" ) type importsSuite struct { testing.CleanupSuite } var _ = gc.Suite(&importsSuite{}) var importsTests = []struct { pkgName string prefix string expect []string }{{ pkgName: "github.com/juju/testing", prefix: "github.com/juju/testing/", expect: []string{"checkers"}, }, { pkgName: "github.com/juju/testing", prefix: "github.com/juju/utils/v4/", expect: []string{}, }, { pkgName: "github.com/juju/testing", prefix: "arble.com/", expect: nil, }} func (s *importsSuite) TestImports(c *gc.C) { for i, test := range importsTests { c.Logf("test %d: %s %s", i, test.pkgName, test.prefix) imports, err := testing.FindImports(test.pkgName, test.prefix) c.Assert(err, gc.IsNil) c.Assert(imports, jc.DeepEquals, test.expect) } } testing-1.2.0/isolation.go000066400000000000000000000016301456110213600155160ustar00rootroot00000000000000// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing import ( gc "gopkg.in/check.v1" ) // IsolationSuite isolates the tests from the underlaying system environment, // sets up test logging and exposes cleanup facilities. type IsolationSuite struct { OsEnvSuite CleanupSuite LoggingSuite } func (s *IsolationSuite) SetUpSuite(c *gc.C) { s.OsEnvSuite.SetUpSuite(c) s.CleanupSuite.SetUpSuite(c) s.LoggingSuite.SetUpSuite(c) } func (s *IsolationSuite) TearDownSuite(c *gc.C) { s.LoggingSuite.TearDownSuite(c) s.CleanupSuite.TearDownSuite(c) s.OsEnvSuite.TearDownSuite(c) } func (s *IsolationSuite) SetUpTest(c *gc.C) { s.OsEnvSuite.SetUpTest(c) s.CleanupSuite.SetUpTest(c) s.LoggingSuite.SetUpTest(c) } func (s *IsolationSuite) TearDownTest(c *gc.C) { s.LoggingSuite.TearDownTest(c) s.CleanupSuite.TearDownTest(c) s.OsEnvSuite.TearDownTest(c) } testing-1.2.0/log.go000066400000000000000000000047531456110213600143070ustar00rootroot00000000000000// Copyright 2012-2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing import ( "flag" "fmt" "os" "path/filepath" "github.com/juju/loggo/v2" gc "gopkg.in/check.v1" ) var logLocation = flag.Bool("loggo.location", false, "Also log the location of the loggo call") // LoggingSuite redirects the juju logger to the test logger // when embedded in a gocheck suite type. type LoggingSuite struct{} type gocheckWriter struct { c *gc.C } var logConfig = func() string { if cfg := os.Getenv("TEST_LOGGING_CONFIG"); cfg != "" { return cfg } return "DEBUG" }() func (w *gocheckWriter) Write(entry loggo.Entry) { filename := filepath.Base(entry.Filename) var message string if *logLocation { message = fmt.Sprintf("%s %s %s:%d %s", entry.Level, entry.Module, filename, entry.Line, entry.Message) } else { message = fmt.Sprintf("%s %s %s", entry.Level, entry.Module, entry.Message) } // Magic calldepth value... // The value says "how far up the call stack do we go to find the location". // It is used to match the standard library log function, and isn't actually // used by gocheck. w.c.Output(3, message) } func (s *LoggingSuite) SetUpSuite(c *gc.C) { s.setUp(c) } func (s *LoggingSuite) TearDownSuite(c *gc.C) { loggo.ResetLogging() } func (s *LoggingSuite) SetUpTest(c *gc.C) { s.setUp(c) } func (s *LoggingSuite) TearDownTest(c *gc.C) { } type discardWriter struct{} func (discardWriter) Write(entry loggo.Entry) { } func (s *LoggingSuite) setUp(c *gc.C) { loggo.ResetLogging() // Don't use the default writer for the test logging, which // means we can still get logging output from tests that // replace the default writer. loggo.RegisterWriter(loggo.DefaultWriterName, discardWriter{}) loggo.RegisterWriter("loggingsuite", &gocheckWriter{c}) err := loggo.ConfigureLoggers(logConfig) c.Assert(err, gc.IsNil) } // LoggingCleanupSuite is defined for backward compatibility. // Do not use this suite in new tests. type LoggingCleanupSuite struct { LoggingSuite CleanupSuite } func (s *LoggingCleanupSuite) SetUpSuite(c *gc.C) { s.CleanupSuite.SetUpSuite(c) s.LoggingSuite.SetUpSuite(c) } func (s *LoggingCleanupSuite) TearDownSuite(c *gc.C) { s.LoggingSuite.TearDownSuite(c) s.CleanupSuite.TearDownSuite(c) } func (s *LoggingCleanupSuite) SetUpTest(c *gc.C) { s.CleanupSuite.SetUpTest(c) s.LoggingSuite.SetUpTest(c) } func (s *LoggingCleanupSuite) TearDownTest(c *gc.C) { s.CleanupSuite.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } testing-1.2.0/log_test.go000066400000000000000000000022171456110213600153370ustar00rootroot00000000000000// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing import ( gc "gopkg.in/check.v1" "github.com/juju/loggo/v2" ) type logSuite struct{} var _ = gc.Suite(&logSuite{}) func (*logSuite) TestLog(c *gc.C) { logger := loggo.GetLogger("test") jujuLogger := loggo.GetLogger("juju") logConfig = "=DEBUG;juju=TRACE" c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.WARNING) var suite LoggingSuite suite.SetUpSuite(c) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.DEBUG) c.Assert(jujuLogger.EffectiveLogLevel(), gc.Equals, loggo.TRACE) logger.Debugf("message 1") logger.Tracef("message 2") jujuLogger.Tracef("message 3") c.Assert(c.GetTestLog(), gc.Matches, ".*DEBUG test message 1\n"+ ".*TRACE juju message 3\n", ) suite.TearDownSuite(c) logger.Debugf("message 1") logger.Tracef("message 2") jujuLogger.Tracef("message 3") c.Assert(c.GetTestLog(), gc.Matches, ".*DEBUG test message 1\n"+ ".*TRACE juju message 3\n$", ) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.WARNING) c.Assert(jujuLogger.EffectiveLogLevel(), gc.Equals, loggo.WARNING) } testing-1.2.0/mock_example_test.go000066400000000000000000000034231456110213600172220ustar00rootroot00000000000000package testing_test import ( "fmt" "log" "github.com/juju/loggo/v2" "github.com/juju/testing" ) type ExampleInterfaceToMock interface { Add(a, b int) int Div(a, b int) (int, error) } type fakeType struct { ExampleInterfaceToMock *testing.CallMocker } func (f *fakeType) Add(a, b int) int { results := f.MethodCall(f, "Add", a, b) return results[0].(int) } func (f *fakeType) Div(a, b int) (int, error) { results := f.MethodCall(f, "Div", a, b) return results[0].(int), testing.TypeAssertError(results[1]) } type ExampleTypeWhichUsesInterface struct { calculator ExampleInterfaceToMock } func (e *ExampleTypeWhichUsesInterface) Add(nums ...int) int { var tally int for n := range nums { tally = e.calculator.Add(tally, n) } return tally } func (e *ExampleTypeWhichUsesInterface) Div(nums ...int) (int, error) { var tally int var err error for n := range nums { tally, err = e.calculator.Div(tally, n) if err != nil { break } } return tally, err } func Example() { var logger loggo.Logger // Set a fake type which mocks out calls. mock := &fakeType{CallMocker: testing.NewCallMocker(logger)} mock.Call("Add", 1, 1).Returns(2) mock.Call("Div", 1, 1).Returns(1, nil) mock.Call("Div", 1, 0).Returns(0, fmt.Errorf("cannot divide by zero")) // Pass in the mock which satisifes a dependency of // ExampleTypeWhichUsesInterface. This allows us to inject mocked // calls. example := ExampleTypeWhichUsesInterface{calculator: mock} if example.Add(1, 1) != 2 { log.Fatal("unexpected result") } if result, err := example.Div(1, 1); err != nil { log.Fatalf("unexpected error: %v", err) } else if result != 1 { log.Fatal("unexpected result") } if _, err := example.Div(1, 0); err == nil { log.Fatal("did not receive expected divide by zero error") } } testing-1.2.0/mocker.go000066400000000000000000000056561456110213600150110ustar00rootroot00000000000000package testing import ( "reflect" "sync" "github.com/juju/loggo/v2" ) // NewCallMocker returns a CallMocker which will log calls and results // utilizing the given logger. func NewCallMocker(logger loggo.Logger) *CallMocker { return &CallMocker{ logger: logger, results: make(map[string][]*callMockReturner), } } // CallMocker is a tool which allows tests to dynamically specify // results for a given set of input parameters. type CallMocker struct { Stub logger loggo.Logger results map[string][]*callMockReturner } // MethodCall logs the call to a method and any results that will be // returned. It returns the results previously specified by the Call // function. If no results were specified, the returned slice will be // nil. func (m *CallMocker) MethodCall(receiver interface{}, fnName string, args ...interface{}) []interface{} { m.Stub.MethodCall(receiver, fnName, args...) m.logger.Debugf("Call: %s(%v)", fnName, args) results := m.Results(fnName, args...) m.logger.Debugf("Results: %v", results) return results } // Results returns any results previously specified by calls to the // Call method. If there are no results, the returned slice will be // nil. func (m *CallMocker) Results(fnName string, args ...interface{}) []interface{} { for _, r := range m.results[fnName] { if reflect.DeepEqual(r.args, args) == false { continue } r.logCall() return r.retVals } return nil } // Call is the first half a chained-predicate which registers that // calls to a function named fnName with arguments args should return // some value. The returned values are handled by the returned type, // callMockReturner. func (m *CallMocker) Call(fnName string, args ...interface{}) *callMockReturner { returner := &callMockReturner{args: args} // Push on the front to hide old results. m.results[fnName] = append([]*callMockReturner{returner}, m.results[fnName]...) return returner } type callMockReturner struct { // args holds a reference to the arguments for which the retVals // are valid. args []interface{} // retVals holds a reference to the values that should be returned // when the values held by args are seen. retVals []interface{} // timesInvoked records the number of times this return has been // reached. timesInvoked struct { sync.Mutex value int } } // Returns declares that this returner should return retVals when // called. It returns a closure which can be called to determine the // number of times this return has happened. func (m *callMockReturner) Returns(retVals ...interface{}) func() int { m.retVals = retVals return m.numTimesInvoked } func (m *callMockReturner) logCall() { m.timesInvoked.Lock() defer m.timesInvoked.Unlock() m.timesInvoked.value++ } func (m *callMockReturner) numTimesInvoked() int { m.timesInvoked.Lock() defer m.timesInvoked.Unlock() return m.timesInvoked.value } func TypeAssertError(err interface{}) error { if err == nil { return nil } return err.(error) } testing-1.2.0/norace.go000066400000000000000000000002321456110213600147610ustar00rootroot00000000000000// Copyright 2015 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. // +build !race package testing const RaceEnabled = false testing-1.2.0/osenv.go000066400000000000000000000061231456110213600146510ustar00rootroot00000000000000// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing import ( "os" "runtime" "strings" gc "gopkg.in/check.v1" ) // OsEnvSuite isolates the tests from the underlaying system environment. // Environment variables are reset in SetUpTest and restored in TearDownTest. type OsEnvSuite struct { oldEnvironment map[string]string } // windowsVariables is a whitelist of windows environment variables // that will be retained if found. Some of these variables are needed // by standard go packages (such as os.TempDir()), as well as powershell var windowsVariables = []string{ "ALLUSERSPROFILE", "APPDATA", "CommonProgramFiles", "CommonProgramFiles(x86)", "CommonProgramW6432", "COMPUTERNAME", "ComSpec", "FP_NO_HOST_CHECK", "HOMEDRIVE", "HOMEPATH", "LOCALAPPDATA", "LOGONSERVER", "NUMBER_OF_PROCESSORS", "OS", "Path", "PATHEXT", "PROCESSOR_ARCHITECTURE", "PROCESSOR_IDENTIFIER", "PROCESSOR_LEVEL", "PROCESSOR_REVISION", "ProgramData", "ProgramFiles", "ProgramFiles(x86)", "ProgramW6432", "PROMPT", "PSModulePath", "PUBLIC", "SESSIONNAME", "SystemDrive", "SystemRoot", "TEMP", "TMP", "USERDOMAIN", "USERDOMAIN_ROAMINGPROFILE", "USERNAME", "USERPROFILE", "windir", } // testingVariables is a whitelist of environment variables // used to control Juju tests, that will be retained if found. var testingVariables = []string{ "JUJU_MONGOD", } func (s *OsEnvSuite) setEnviron() { var isWhitelisted func(string) bool switch runtime.GOOS { case "windows": // Lowercase variable names for comparison as they are case // insenstive on windows. Fancy folding not required for ascii. lowerEnv := make(map[string]struct{}, len(windowsVariables)+len(testingVariables)) for _, envVar := range windowsVariables { lowerEnv[strings.ToLower(envVar)] = struct{}{} } for _, envVar := range testingVariables { lowerEnv[strings.ToLower(envVar)] = struct{}{} } isWhitelisted = func(envVar string) bool { _, ok := lowerEnv[strings.ToLower(envVar)] return ok } default: isWhitelisted = func(envVar string) bool { for _, testingVar := range testingVariables { if testingVar == envVar { return true } } return false } } for envVar, value := range s.oldEnvironment { if isWhitelisted(envVar) { os.Setenv(envVar, value) } } } // osDependendClearenv will clear the environment, and based on platform, will repopulate // with whitelisted values previously saved in s.oldEnvironment func (s *OsEnvSuite) osDependendClearenv() { os.Clearenv() // Restore any platform required or juju testing variables. s.setEnviron() } func (s *OsEnvSuite) SetUpSuite(c *gc.C) { s.oldEnvironment = make(map[string]string) for _, envvar := range os.Environ() { parts := strings.SplitN(envvar, "=", 2) s.oldEnvironment[parts[0]] = parts[1] } s.osDependendClearenv() } func (s *OsEnvSuite) TearDownSuite(c *gc.C) { os.Clearenv() for name, value := range s.oldEnvironment { os.Setenv(name, value) } } func (s *OsEnvSuite) SetUpTest(c *gc.C) { s.osDependendClearenv() } func (s *OsEnvSuite) TearDownTest(c *gc.C) { } testing-1.2.0/osenv_test.go000066400000000000000000000052331456110213600157110ustar00rootroot00000000000000// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing_test import ( "os" "runtime" gc "gopkg.in/check.v1" "github.com/juju/testing" ) type osEnvSuite struct { osEnvSuite testing.OsEnvSuite } var _ = gc.Suite(&osEnvSuite{}) func (s *osEnvSuite) SetUpSuite(c *gc.C) { s.osEnvSuite = testing.OsEnvSuite{} } func (s *osEnvSuite) TestOriginalEnvironment(c *gc.C) { // The original environment is properly cleaned and restored. err := os.Setenv("TESTING_OSENV_ORIGINAL", "original-value") c.Assert(err, gc.IsNil) s.osEnvSuite.SetUpSuite(c) c.Assert(os.Getenv("TESTING_OSENV_ORIGINAL"), gc.Equals, "") s.osEnvSuite.TearDownSuite(c) // The environment has been restored. c.Assert(os.Getenv("TESTING_OSENV_ORIGINAL"), gc.Equals, "original-value") } func (s *osEnvSuite) TestTestingEnvironment(c *gc.C) { // Environment variables set up by tests are properly removed. s.osEnvSuite.SetUpSuite(c) s.osEnvSuite.SetUpTest(c) err := os.Setenv("TESTING_OSENV_NEW", "new-value") c.Assert(err, gc.IsNil) s.osEnvSuite.TearDownTest(c) s.osEnvSuite.TearDownSuite(c) c.Assert(os.Getenv("TESTING_OSENV_NEW"), gc.Equals, "") } func (s *osEnvSuite) TestPreservesTestingVariables(c *gc.C) { err := os.Setenv("JUJU_MONGOD", "preserved-value") c.Assert(err, gc.IsNil) s.osEnvSuite.SetUpSuite(c) s.osEnvSuite.SetUpTest(c) c.Assert(os.Getenv("JUJU_MONGOD"), gc.Equals, "preserved-value") c.Assert(err, gc.IsNil) s.osEnvSuite.TearDownTest(c) s.osEnvSuite.TearDownSuite(c) c.Assert(os.Getenv("JUJU_MONGOD"), gc.Equals, "preserved-value") } func (s *osEnvSuite) TestRestoresTestingVariables(c *gc.C) { os.Clearenv() s.osEnvSuite.SetUpSuite(c) s.osEnvSuite.SetUpTest(c) err := os.Setenv("JUJU_MONGOD", "test-value") c.Assert(err, gc.IsNil) s.osEnvSuite.TearDownTest(c) s.osEnvSuite.TearDownSuite(c) c.Assert(os.Getenv("JUJU_MONGOD"), gc.Equals, "") } func (s *osEnvSuite) TestWindowsPreservesPath(c *gc.C) { if runtime.GOOS != "windows" { c.Skip("Windows-specific test case") } err := os.Setenv("PATH", "/new/path") c.Assert(err, gc.IsNil) s.osEnvSuite.SetUpSuite(c) s.osEnvSuite.SetUpTest(c) c.Assert(os.Getenv("PATH"), gc.Equals, "/new/path") s.osEnvSuite.TearDownTest(c) s.osEnvSuite.TearDownSuite(c) c.Assert(os.Getenv("PATH"), gc.Equals, "/new/path") } func (s *osEnvSuite) TestWindowsRestoresPath(c *gc.C) { if runtime.GOOS != "windows" { c.Skip("Windows-specific test case") } os.Clearenv() s.osEnvSuite.SetUpSuite(c) s.osEnvSuite.SetUpTest(c) err := os.Setenv("PATH", "/test/path") c.Assert(err, gc.IsNil) s.osEnvSuite.TearDownTest(c) s.osEnvSuite.TearDownSuite(c) c.Assert(os.Getenv("PATH"), gc.Equals, "") } testing-1.2.0/package_test.go000066400000000000000000000003441456110213600161500ustar00rootroot00000000000000// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing_test import ( stdtesting "testing" gc "gopkg.in/check.v1" ) func Test(t *stdtesting.T) { gc.TestingT(t) } testing-1.2.0/patch.go000066400000000000000000000040041456110213600146120ustar00rootroot00000000000000// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing import ( "os" "reflect" "strings" ) // Restorer holds a function that can be used // to restore some previous state. type Restorer func() // Add returns a Restorer that restores first f1 // and then f. It is valid to call this on a nil // Restorer. func (f Restorer) Add(f1 Restorer) Restorer { return func() { f1.Restore() if f != nil { f.Restore() } } } // Restore restores some previous state. func (r Restorer) Restore() { r() } // PatchValue sets the value pointed to by the given destination to the given // value, and returns a function to restore it to its original value. The // value must be assignable to the element type of the destination. func PatchValue(dest, value interface{}) Restorer { destv := reflect.ValueOf(dest).Elem() oldv := reflect.New(destv.Type()).Elem() oldv.Set(destv) valuev := reflect.ValueOf(value) if !valuev.IsValid() { // This isn't quite right when the destination type is not // nilable, but it's better than the complex alternative. valuev = reflect.Zero(destv.Type()) } destv.Set(valuev) return func() { destv.Set(oldv) } } // PatchEnvironment provides a test a simple way to override a single // environment variable. A function is returned that will return the // environment to what it was before. func PatchEnvironment(name, value string) Restorer { oldValue, oldValueSet := os.LookupEnv(name) _ = os.Setenv(name, value) return func() { if oldValueSet { _ = os.Setenv(name, oldValue) } else { _ = os.Unsetenv(name) } } } // PatchEnvPathPrepend provides a simple way to prepend path to the start of the // PATH environment variable. Returns a function that restores the environment // to what it was before. func PatchEnvPathPrepend(dir string) Restorer { return PatchEnvironment("PATH", joinPathLists(dir, os.Getenv("PATH"))) } func joinPathLists(paths ...string) string { return strings.Join(paths, string(os.PathListSeparator)) } testing-1.2.0/patch_test.go000066400000000000000000000064151456110213600156610ustar00rootroot00000000000000// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing_test import ( "errors" "os" gc "gopkg.in/check.v1" "github.com/juju/testing" ) type PatchValueSuite struct{} var _ = gc.Suite(&PatchValueSuite{}) func (*PatchValueSuite) TestSetInt(c *gc.C) { i := 99 restore := testing.PatchValue(&i, 88) c.Assert(i, gc.Equals, 88) restore() c.Assert(i, gc.Equals, 99) } func (*PatchValueSuite) TestSetError(c *gc.C) { oldErr := errors.New("foo") newErr := errors.New("bar") err := oldErr restore := testing.PatchValue(&err, newErr) c.Assert(err, gc.Equals, newErr) restore() c.Assert(err, gc.Equals, oldErr) } func (*PatchValueSuite) TestSetErrorToNil(c *gc.C) { oldErr := errors.New("foo") err := oldErr restore := testing.PatchValue(&err, nil) c.Assert(err, gc.Equals, nil) restore() c.Assert(err, gc.Equals, oldErr) } func (*PatchValueSuite) TestSetMapToNil(c *gc.C) { oldMap := map[string]int{"foo": 1234} m := oldMap restore := testing.PatchValue(&m, nil) c.Assert(m, gc.IsNil) restore() c.Assert(m, gc.DeepEquals, oldMap) } func (*PatchValueSuite) TestSetPanicsWhenNotAssignable(c *gc.C) { i := 99 type otherInt int c.Assert(func() { testing.PatchValue(&i, otherInt(88)) }, gc.PanicMatches, `reflect\.Set: value of type testing_test\.otherInt is not assignable to type int`) } type PatchEnvironmentSuite struct{} var _ = gc.Suite(&PatchEnvironmentSuite{}) func (*PatchEnvironmentSuite) TestPatchEnvironment(c *gc.C) { const envName = "TESTING_PATCH_ENVIRONMENT" // remember the old value, and set it to something we can check oldValue, oldValueSet := os.LookupEnv(envName) defer func() { if oldValueSet { _ = os.Setenv(envName, oldValue) } else { _ = os.Unsetenv(envName) } }() _ = os.Setenv(envName, "initial") restore := testing.PatchEnvironment(envName, "new value") // Using check to make sure the environment gets set back properly in the test. c.Check(os.Getenv(envName), gc.Equals, "new value") restore() c.Check(os.Getenv(envName), gc.Equals, "initial") } func (*PatchEnvironmentSuite) TestPatchEnvironmentWithAbsentVar(c *gc.C) { const envName = "TESTING_PATCH_ENVIRONMENT" // remember the old value, and unset the var oldValue, oldValueSet := os.LookupEnv(envName) defer func() { if oldValueSet { _ = os.Setenv(envName, oldValue) } }() _ = os.Unsetenv(envName) restore := testing.PatchEnvironment(envName, "new value") c.Check(os.Getenv(envName), gc.Equals, "new value") restore() _, set := os.LookupEnv(envName) c.Check(set, gc.Equals, false) } func (*PatchEnvironmentSuite) TestRestorerAdd(c *gc.C) { var order []string first := testing.Restorer(func() { order = append(order, "first") }) second := testing.Restorer(func() { order = append(order, "second") }) restore := first.Add(second) restore() c.Assert(order, gc.DeepEquals, []string{"second", "first"}) } func (*PatchEnvironmentSuite) TestPatchEnvPathPrepend(c *gc.C) { oldPath := os.Getenv("PATH") dir := "/bin/bar" // just in case something goes wrong defer func() { _ = os.Setenv("PATH", oldPath) }() restore := testing.PatchEnvPathPrepend(dir) expect := dir + string(os.PathListSeparator) + oldPath c.Check(os.Getenv("PATH"), gc.Equals, expect) restore() c.Check(os.Getenv("PATH"), gc.Equals, oldPath) } testing-1.2.0/race.go000066400000000000000000000002301456110213600144220ustar00rootroot00000000000000// Copyright 2015 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. // +build race package testing const RaceEnabled = true testing-1.2.0/stub.go000066400000000000000000000213121456110213600144710ustar00rootroot00000000000000// Copyright 2015 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing import ( "fmt" "reflect" "sync" jc "github.com/juju/testing/checkers" gc "gopkg.in/check.v1" ) // StubCall records the name of a called function and the passed args. type StubCall struct { // Funcname is the name of the function that was called. FuncName string // Args is the set of arguments passed to the function. They are // in the same order as the function's parameters Args []interface{} } // Stub is used in testing to stand in for some other value, to record // all calls to stubbed methods/functions, and to allow users to set the // values that are returned from those calls. Stub is intended to be // embedded in another struct that will define the methods to track: // // type stubConn struct { // *testing.Stub // Response []byte // } // // func newStubConn() *stubConn { // return &stubConn{ // Stub: &testing.Stub{}, // } // } // // // Send implements Connection. // func (fc *stubConn) Send(request string) []byte { // fc.MethodCall(fc, "Send", request) // return fc.Response, fc.NextErr() // } // // As demonstrated in the example, embed a pointer to testing.Stub. This // allows a single testing.Stub to be shared between multiple stubs. // // Error return values are set through Stub.Errors. Set it to the errors // you want returned (or use the convenience method `SetErrors`). The // `NextErr` method returns the errors from Stub.Errors in sequence, // falling back to `DefaultError` when the sequence is exhausted. Thus // each stubbed method should call `NextErr` to get its error return value. // // To validate calls made to the stub in a test call the CheckCalls // (or CheckCall) method: // // s.stub.CheckCalls(c, []StubCall{{ // FuncName: "Send", // Args: []interface{}{ // expected, // }, // }}) // // s.stub.CheckCall(c, 0, "Send", expected) // // Not only is Stub useful for building a interface implementation to // use in testing (e.g. a network API client), it is also useful in // regular function patching situations: // // type myStub struct { // *testing.Stub // } // // func (f *myStub) SomeFunc(arg interface{}) error { // f.AddCall("SomeFunc", arg) // return f.NextErr() // } // // s.PatchValue(&somefunc, s.myStub.SomeFunc) // // This allows for easily monitoring the args passed to the patched // func, as well as controlling the return value from the func in a // clean manner (by simply setting the correct field on the stub). type Stub struct { mu sync.Mutex // serialises access the to following fields // calls is the list of calls that have been registered on the stub // (i.e. made on the stub's methods), in the order that they were // made. calls []StubCall // receivers is the list of receivers for all the recorded calls. // In the case of non-methods, the receiver is set to nil. The // receivers are tracked here rather than as a Receiver field on // StubCall because StubCall represents the common case for // testing. Typically the receiver does not need to be checked. receivers []interface{} // errors holds the list of error return values to use for // successive calls to methods that return an error. Each call // pops the next error off the list. An empty list (the default) // implies a nil error. nil may be precede actual errors in the // list, which means that the first calls will succeed, followed // by the failure. All this is facilitated through the Err method. errors []error } // TODO(ericsnow) Add something similar to NextErr for all return values // using reflection? // NextErr returns the error that should be returned on the nth call to // any method on the stub. It should be called for the error return in // all stubbed methods. func (f *Stub) NextErr() error { f.mu.Lock() defer f.mu.Unlock() if len(f.errors) == 0 { return nil } err := f.errors[0] f.errors = f.errors[1:] return err } // PopNoErr pops off the next error without returning it. If the error // is not nil then PopNoErr will panic. // // PopNoErr is useful in stub methods that do not return an error. func (f *Stub) PopNoErr() { if err := f.NextErr(); err != nil { panic(fmt.Sprintf("expected a nil error, got %v", err)) } } func (f *Stub) addCall(rcvr interface{}, funcName string, args []interface{}) { f.mu.Lock() defer f.mu.Unlock() f.calls = append(f.calls, StubCall{ FuncName: funcName, Args: args, }) f.receivers = append(f.receivers, rcvr) } // Calls returns the list of calls that have been registered on the stub // (i.e. made on the stub's methods), in the order that they were made. func (f *Stub) Calls() []StubCall { f.mu.Lock() defer f.mu.Unlock() v := make([]StubCall, len(f.calls)) copy(v, f.calls) return v } // ResetCalls erases the calls recorded by this Stub. func (f *Stub) ResetCalls() { f.mu.Lock() defer f.mu.Unlock() f.calls = nil } // AddCall records a stubbed function call for later inspection using the // CheckCalls method. A nil receiver is recorded. Thus for methods use // MethodCall. All stubbed functions should call AddCall. func (f *Stub) AddCall(funcName string, args ...interface{}) { f.addCall(nil, funcName, args) } // MethodCall records a stubbed method call for later inspection using // the CheckCalls method. The receiver is added to Stub.Receivers. func (f *Stub) MethodCall(receiver interface{}, funcName string, args ...interface{}) { f.addCall(receiver, funcName, args) } // SetErrors sets the sequence of error returns for the stub. Each call // to Err (thus each stub method call) pops an error off the front. So // frontloading nil here will allow calls to pass, followed by a // failure. func (f *Stub) SetErrors(errors ...error) { f.mu.Lock() defer f.mu.Unlock() f.errors = errors } // CheckCalls verifies that the history of calls on the stub's methods // matches the expected calls. The receivers are not checked. If they // are significant then check Stub.Receivers separately. func (f *Stub) CheckCalls(c *gc.C, expected []StubCall) { if !f.CheckCallNames(c, stubCallNames(expected...)...) { return } c.Check(f.calls, jc.DeepEquals, expected) } // CheckCallsUnordered verifies that the history of calls on the stub's methods // contains the expected calls. The receivers are not checked. If they // are significant then check Stub.Receivers separately. // This method explicitly does not check if the calls were made in order, just // whether they have been made. func (f *Stub) CheckCallsUnordered(c *gc.C, expected []StubCall) { // Take a copy of all calls made to the stub. calls := f.calls[:] checkCallMade := func(call StubCall) { for i, madeCall := range calls { if reflect.DeepEqual(call, madeCall) { // Remove found call from the copy of all-calls-made collection. calls = append(calls[:i], calls[i+1:]...) break } } } for _, call := range expected { checkCallMade(call) } // If all expected calls were made, our resulting collection should be empty. c.Check(calls, gc.DeepEquals, []StubCall{}) } // CheckCall checks the recorded call at the given index against the // provided values. If the index is out of bounds then the check fails. // The receiver is not checked. If it is significant for a test then it // can be checked separately: // // c.Check(mystub.Receivers[index], gc.Equals, expected) func (f *Stub) CheckCall(c *gc.C, index int, funcName string, args ...interface{}) { f.mu.Lock() defer f.mu.Unlock() if !c.Check(index, jc.LessThan, len(f.calls)) { return } call := f.calls[index] expected := StubCall{ FuncName: funcName, Args: args, } c.Check(call, jc.DeepEquals, expected) } // CheckCallNames verifies that the in-order list of called method names // matches the expected calls. func (f *Stub) CheckCallNames(c *gc.C, expected ...string) bool { f.mu.Lock() defer f.mu.Unlock() funcNames := stubCallNames(f.calls...) return c.Check(funcNames, jc.DeepEquals, expected) } // CheckNoCalls verifies that none of the stub's methods have been called. func (f *Stub) CheckNoCalls(c *gc.C) { f.CheckCalls(c, nil) } // CheckErrors verifies that the list of errors is matches the expected list. func (f *Stub) CheckErrors(c *gc.C, expected ...error) bool { f.mu.Lock() defer f.mu.Unlock() return c.Check(f.errors, jc.DeepEquals, expected) } // CheckReceivers verifies that the list of errors is matches the expected list. func (f *Stub) CheckReceivers(c *gc.C, expected ...interface{}) bool { f.mu.Lock() defer f.mu.Unlock() return c.Check(f.receivers, jc.DeepEquals, expected) } func stubCallNames(calls ...StubCall) []string { var funcNames []string for _, call := range calls { funcNames = append(funcNames, call.FuncName) } return funcNames } testing-1.2.0/stub_test.go000066400000000000000000000261621456110213600155400ustar00rootroot00000000000000// Copyright 2015 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing_test import ( "github.com/juju/errors" gc "gopkg.in/check.v1" "github.com/juju/testing" jc "github.com/juju/testing/checkers" ) type stubA struct { *testing.Stub } func (f *stubA) aMethod(a, b, c int) error { f.MethodCall(f, "aMethod", a, b, c) return f.NextErr() } func (f *stubA) otherMethod(values ...string) error { f.MethodCall(f, "otherMethod", values) return f.NextErr() } type stubB struct { *testing.Stub } func (f *stubB) aMethod() error { f.MethodCall(f, "aMethod") return f.NextErr() } func (f *stubB) aFunc(value string) error { f.AddCall("aFunc", value) return f.NextErr() } type stubSuite struct { stub *testing.Stub } var _ = gc.Suite(&stubSuite{}) func (s *stubSuite) SetUpTest(c *gc.C) { s.stub = &testing.Stub{} } func (s *stubSuite) TestNextErrSequence(c *gc.C) { exp1 := errors.New("") exp2 := errors.New("") s.stub.SetErrors(exp1, exp2) err1 := s.stub.NextErr() err2 := s.stub.NextErr() c.Check(err1, gc.Equals, exp1) c.Check(err2, gc.Equals, exp2) } func (s *stubSuite) TestNextErrPops(c *gc.C) { exp1 := errors.New("") exp2 := errors.New("") s.stub.SetErrors(exp1, exp2) s.stub.NextErr() s.stub.CheckErrors(c, exp2) } func (s *stubSuite) TestNextErrEmptyNil(c *gc.C) { err1 := s.stub.NextErr() err2 := s.stub.NextErr() c.Check(err1, jc.ErrorIsNil) c.Check(err2, jc.ErrorIsNil) } func (s *stubSuite) TestNextErrSkip(c *gc.C) { expected := errors.New("") s.stub.SetErrors(nil, nil, expected) err1 := s.stub.NextErr() err2 := s.stub.NextErr() err3 := s.stub.NextErr() c.Check(err1, jc.ErrorIsNil) c.Check(err2, jc.ErrorIsNil) c.Check(err3, gc.Equals, expected) } func (s *stubSuite) TestNextErrEmbeddedMixed(c *gc.C) { exp1 := errors.New("") exp2 := errors.New("") s.stub.SetErrors(exp1, nil, nil, exp2) stub1 := &stubA{s.stub} stub2 := &stubB{s.stub} err1 := stub1.aMethod(1, 2, 3) err2 := stub2.aFunc("arg") err3 := stub1.otherMethod("arg1", "arg2") err4 := stub2.aMethod() c.Check(err1, gc.Equals, exp1) c.Check(err2, jc.ErrorIsNil) c.Check(err3, jc.ErrorIsNil) c.Check(err4, gc.Equals, exp2) } func (s *stubSuite) TestPopNoErrOkay(c *gc.C) { exp1 := errors.New("") exp2 := errors.New("") s.stub.SetErrors(exp1, nil, exp2) err1 := s.stub.NextErr() s.stub.PopNoErr() err2 := s.stub.NextErr() c.Check(err1, gc.Equals, exp1) c.Check(err2, gc.Equals, exp2) } func (s *stubSuite) TestPopNoErrEmpty(c *gc.C) { s.stub.PopNoErr() err := s.stub.NextErr() c.Check(err, jc.ErrorIsNil) } func (s *stubSuite) TestPopNoErrPanic(c *gc.C) { failure := errors.New("") s.stub.SetErrors(failure) f := func() { s.stub.PopNoErr() } c.Check(f, gc.PanicMatches, `expected a nil error, got .*`) } func (s *stubSuite) TestAddCallRecorded(c *gc.C) { s.stub.AddCall("aFunc", 1, 2, 3) c.Check(s.stub.Calls(), jc.DeepEquals, []testing.StubCall{{ FuncName: "aFunc", Args: []interface{}{1, 2, 3}, }}) s.stub.CheckReceivers(c, nil) } func (s *stubSuite) TestAddCallRepeated(c *gc.C) { s.stub.AddCall("before", "arg") s.stub.AddCall("aFunc", 1, 2, 3) s.stub.AddCall("aFunc", 4, 5, 6) s.stub.AddCall("after", "arg") c.Check(s.stub.Calls(), jc.DeepEquals, []testing.StubCall{{ FuncName: "before", Args: []interface{}{"arg"}, }, { FuncName: "aFunc", Args: []interface{}{1, 2, 3}, }, { FuncName: "aFunc", Args: []interface{}{4, 5, 6}, }, { FuncName: "after", Args: []interface{}{"arg"}, }}) s.stub.CheckReceivers(c, nil, nil, nil, nil) } func (s *stubSuite) TestAddCallNoArgs(c *gc.C) { s.stub.AddCall("aFunc") c.Check(s.stub.Calls(), jc.DeepEquals, []testing.StubCall{{ FuncName: "aFunc", }}) } func (s *stubSuite) TestResetCalls(c *gc.C) { s.stub.AddCall("aFunc") s.stub.CheckCalls(c, []testing.StubCall{{FuncName: "aFunc"}}) s.stub.ResetCalls() s.stub.CheckCalls(c, nil) } func (s *stubSuite) TestAddCallSequence(c *gc.C) { s.stub.AddCall("first") s.stub.AddCall("second") s.stub.AddCall("third") c.Check(s.stub.Calls(), jc.DeepEquals, []testing.StubCall{{ FuncName: "first", }, { FuncName: "second", }, { FuncName: "third", }}) } func (s *stubSuite) TestMethodCallRecorded(c *gc.C) { s.stub.MethodCall(s.stub, "aMethod", 1, 2, 3) c.Check(s.stub.Calls(), jc.DeepEquals, []testing.StubCall{{ FuncName: "aMethod", Args: []interface{}{1, 2, 3}, }}) s.stub.CheckReceivers(c, s.stub) } func (s *stubSuite) TestMethodCallMixed(c *gc.C) { s.stub.MethodCall(s.stub, "Method1", 1, 2, 3) s.stub.AddCall("aFunc", "arg") s.stub.MethodCall(s.stub, "Method2") s.stub.CheckCalls(c, []testing.StubCall{{ FuncName: "Method1", Args: []interface{}{1, 2, 3}, }, { FuncName: "aFunc", Args: []interface{}{"arg"}, }, { FuncName: "Method2", }}) s.stub.CheckReceivers(c, s.stub, nil, s.stub) } func (s *stubSuite) TestMethodCallEmbeddedMixed(c *gc.C) { stub1 := &stubA{s.stub} stub2 := &stubB{s.stub} err := stub1.aMethod(1, 2, 3) c.Assert(err, jc.ErrorIsNil) err = stub2.aFunc("arg") c.Assert(err, jc.ErrorIsNil) err = stub1.otherMethod("arg1", "arg2") c.Assert(err, jc.ErrorIsNil) err = stub2.aMethod() c.Assert(err, jc.ErrorIsNil) c.Check(s.stub.Calls(), jc.DeepEquals, []testing.StubCall{{ FuncName: "aMethod", Args: []interface{}{1, 2, 3}, }, { FuncName: "aFunc", Args: []interface{}{"arg"}, }, { FuncName: "otherMethod", Args: []interface{}{[]string{"arg1", "arg2"}}, }, { FuncName: "aMethod", }}) s.stub.CheckReceivers(c, stub1, nil, stub1, stub2) } func (s *stubSuite) TestSetErrorsMultiple(c *gc.C) { err1 := errors.New("") err2 := errors.New("") s.stub.SetErrors(err1, err2) s.stub.CheckErrors(c, err1, err2) } func (s *stubSuite) TestSetErrorsEmpty(c *gc.C) { s.stub.SetErrors() // pass an empty varargs of errors s.stub.CheckErrors(c) // check that it is indeed empty } func (s *stubSuite) TestSetErrorMixed(c *gc.C) { err1 := errors.New("") err2 := errors.New("") s.stub.SetErrors(nil, err1, nil, err2) s.stub.CheckErrors(c, nil, err1, nil, err2) } func (s *stubSuite) TestSetErrorsTrailingNil(c *gc.C) { err := errors.New("") s.stub.SetErrors(err, nil) s.stub.CheckErrors(c, err, nil) } func (s *stubSuite) checkCallsStandard(c *gc.C) { s.stub.CheckCalls(c, []testing.StubCall{{ FuncName: "first", Args: []interface{}{"arg"}, }, { FuncName: "second", Args: []interface{}{1, 2, 3}, }, { FuncName: "third", }}) } func (s *stubSuite) TestCheckCallsPass(c *gc.C) { s.stub.AddCall("first", "arg") s.stub.AddCall("second", 1, 2, 3) s.stub.AddCall("third") s.checkCallsStandard(c) } func (s *stubSuite) TestCheckCallsEmpty(c *gc.C) { s.stub.CheckCalls(c, nil) } func (s *stubSuite) TestCheckCallsMissingCall(c *gc.C) { s.stub.AddCall("first", "arg") s.stub.AddCall("third") c.ExpectFailure(`the "standard" Stub.CheckCalls call should fail`) s.checkCallsStandard(c) } func (s *stubSuite) TestCheckCallsWrongName(c *gc.C) { s.stub.AddCall("first", "arg") s.stub.AddCall("oops", 1, 2, 3) s.stub.AddCall("third") c.ExpectFailure(`the "standard" Stub.CheckCalls call should fail`) s.checkCallsStandard(c) } func (s *stubSuite) TestCheckCallsWrongArgs(c *gc.C) { s.stub.AddCall("first", "arg") s.stub.AddCall("second", 1, 2, 4) s.stub.AddCall("third") c.ExpectFailure(`the "standard" Stub.CheckCalls call should fail`) s.checkCallsStandard(c) } func (s *stubSuite) checkCallStandard(c *gc.C) { s.stub.CheckCall(c, 0, "first", "arg") s.stub.CheckCall(c, 1, "second", 1, 2, 3) s.stub.CheckCall(c, 2, "third") } func (s *stubSuite) TestCheckCallPass(c *gc.C) { s.stub.AddCall("first", "arg") s.stub.AddCall("second", 1, 2, 3) s.stub.AddCall("third") s.checkCallStandard(c) } func (s *stubSuite) TestCheckCallEmpty(c *gc.C) { c.ExpectFailure(`Stub.CheckCall should fail when no calls have been made`) s.stub.CheckCall(c, 0, "aMethod") } func (s *stubSuite) TestCheckCallMissingCall(c *gc.C) { s.stub.AddCall("first", "arg") s.stub.AddCall("third") c.ExpectFailure(`the "standard" Stub.CheckCall call should fail here`) s.checkCallStandard(c) } func (s *stubSuite) TestCheckCallWrongName(c *gc.C) { s.stub.AddCall("first", "arg") s.stub.AddCall("oops", 1, 2, 3) s.stub.AddCall("third") c.ExpectFailure(`the "standard" Stub.CheckCall call should fail here`) s.checkCallStandard(c) } func (s *stubSuite) TestCheckCallWrongArgs(c *gc.C) { s.stub.AddCall("first", "arg") s.stub.AddCall("second", 1, 2, 4) s.stub.AddCall("third") c.ExpectFailure(`the "standard" Stub.CheckCall call should fail here`) s.checkCallStandard(c) } func (s *stubSuite) TestCheckCallNamesPass(c *gc.C) { s.stub.AddCall("first", "arg") s.stub.AddCall("second", 1, 2, 4) s.stub.AddCall("third") s.stub.CheckCallNames(c, "first", "second", "third") } func (s *stubSuite) TestCheckCallNamesUnexpected(c *gc.C) { s.stub.AddCall("first", "arg") s.stub.AddCall("second", 1, 2, 4) s.stub.AddCall("third") c.ExpectFailure(`Stub.CheckCall should fail when no calls have been made`) s.stub.CheckCallNames(c) } func (s *stubSuite) TestCheckCallNamesEmptyPass(c *gc.C) { s.stub.CheckCallNames(c) } func (s *stubSuite) TestCheckCallNamesEmptyFail(c *gc.C) { c.ExpectFailure(`Stub.CheckCall should fail when no calls have been made`) s.stub.CheckCallNames(c, "aMethod") } func (s *stubSuite) TestCheckCallNamesMissingCall(c *gc.C) { s.stub.AddCall("first", "arg") s.stub.AddCall("third") c.ExpectFailure(`the "standard" Stub.CheckCallNames call should fail here`) s.stub.CheckCallNames(c, "first", "second", "third") } func (s *stubSuite) TestCheckCallNamesWrongName(c *gc.C) { s.stub.AddCall("first", "arg") s.stub.AddCall("oops", 1, 2, 4) s.stub.AddCall("third") c.ExpectFailure(`the "standard" Stub.CheckCallNames call should fail here`) s.stub.CheckCallNames(c, "first", "second", "third") } func (s *stubSuite) TestCheckNoCalls(c *gc.C) { s.stub.CheckNoCalls(c) s.stub.AddCall("method", "arg") c.ExpectFailure(`the "standard" Stub.CheckNoCalls call should fail here`) s.stub.CheckNoCalls(c) } func (s *stubSuite) TestMethodCallsUnordered(c *gc.C) { s.stub.MethodCall(s.stub, "Method1", 1, 2, 3) s.stub.AddCall("aFunc", "arg") s.stub.MethodCall(s.stub, "Method2") s.stub.CheckCallsUnordered(c, []testing.StubCall{{ FuncName: "aFunc", Args: []interface{}{"arg"}, }, { FuncName: "Method1", Args: []interface{}{1, 2, 3}, }, { FuncName: "Method2", }}) } // This case checks that in the scenario when expected calls are // [a,b,c,c] but the calls made are actually [a,b,b,c], we fail correctly. func (s *stubSuite) TestMethodCallsUnorderedDuplicateFail(c *gc.C) { s.stub.MethodCall(s.stub, "Method1", 1, 2, 3) s.stub.MethodCall(s.stub, "Method1", 1, 2, 3) s.stub.AddCall("aFunc", "arg") s.stub.MethodCall(s.stub, "Method2") s.stub.CheckCallsUnordered(c, []testing.StubCall{{ FuncName: "aFunc", Args: []interface{}{"arg"}, }, { FuncName: "Method1", Args: []interface{}{1, 2, 3}, }, { FuncName: "Method2", }, { FuncName: "Method2", }}) c.ExpectFailure("should have failed as expected calls differ from calls made") } testing-1.2.0/tcpproxy.go000066400000000000000000000064601456110213600154130ustar00rootroot00000000000000// Copyright 2015 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing import ( "io" "net" "sync" jc "github.com/juju/testing/checkers" gc "gopkg.in/check.v1" ) // TCPProxy is a simple TCP proxy that can be used // to deliberately break TCP connections. type TCPProxy struct { listener net.Listener // mu guards the fields below it. mu sync.Mutex // stopStart holds a condition variable that broadcasts changes // in the paused state. stopStart sync.Cond // closed holds whether the proxy has been closed. closed bool // paused holds whether the proxy has been paused. paused bool // conns holds all connections that have been made. conns []io.Closer } // NewTCPProxy runs a proxy that copies to and from // the given remote TCP address. When the proxy // is closed, its listener and all connections will be closed. func NewTCPProxy(c *gc.C, remoteAddr string) *TCPProxy { listener, err := net.Listen("tcp", "127.0.0.1:0") c.Assert(err, jc.ErrorIsNil) p := &TCPProxy{ listener: listener, } p.stopStart.L = &p.mu go func() { for { client, err := p.listener.Accept() if err != nil { if !p.isClosed() { c.Errorf("cannot accept: %v", err) } return } p.addConn(client) server, err := net.Dial("tcp", remoteAddr) if err != nil { if !p.isClosed() { c.Errorf("cannot dial remote address: %v", err) } return } p.addConn(server) go p.stream(client, server) go p.stream(server, client) } }() return p } func (p *TCPProxy) addConn(c net.Conn) { p.mu.Lock() defer p.mu.Unlock() if p.closed { c.Close() } else { p.conns = append(p.conns, c) } } // Close closes the TCPProxy and any connections that // are currently active. func (p *TCPProxy) Close() error { p.mu.Lock() defer p.mu.Unlock() p.closed = true p.listener.Close() for _, c := range p.conns { c.Close() } return nil } // CloseConns closes all the connections that are // currently active. The proxy itself remains active. func (p *TCPProxy) CloseConns() { p.mu.Lock() defer p.mu.Unlock() for _, c := range p.conns { c.Close() } } // PauseConns stops all traffic flowing through the proxy. func (p *TCPProxy) PauseConns() { p.mu.Lock() defer p.mu.Unlock() p.paused = true p.stopStart.Broadcast() } // ResumeConns resumes sending traffic through the proxy. func (p *TCPProxy) ResumeConns() { p.mu.Lock() defer p.mu.Unlock() p.paused = false p.stopStart.Broadcast() } // Addr returns the TCP address of the proxy. Dialing // this address will cause a connection to be made // to the remote address; any data written will be // written there, and any data read from the remote // address will be available to read locally. func (p *TCPProxy) Addr() string { // Note: this only works because we explicitly listen on 127.0.0.1 rather // than the wildcard address. return p.listener.Addr().String() } func (p *TCPProxy) isClosed() bool { p.mu.Lock() defer p.mu.Unlock() return p.closed } func (p *TCPProxy) stream(dst io.WriteCloser, src io.ReadCloser) { defer dst.Close() defer src.Close() buf := make([]byte, 32*1024) for { nr, err := src.Read(buf) p.mu.Lock() for p.paused { p.stopStart.Wait() } p.mu.Unlock() if nr > 0 { _, err := dst.Write(buf[0:nr]) if err != nil { break } } if err != nil { break } } } testing-1.2.0/tcpproxy_test.go000066400000000000000000000106621456110213600164510ustar00rootroot00000000000000// Copyright 2015 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing_test import ( "fmt" "io" "net" "sync" "time" "github.com/juju/testing" gc "gopkg.in/check.v1" ) var _ = gc.Suite(&tcpProxySuite{}) type tcpProxySuite struct{} func (*tcpProxySuite) TestTCPProxy(c *gc.C) { var wg sync.WaitGroup listener, err := net.Listen("tcp", "127.0.0.1:0") c.Assert(err, gc.IsNil) defer listener.Close() wg.Add(1) go tcpEcho(&wg, listener) p := testing.NewTCPProxy(c, listener.Addr().String()) c.Assert(p.Addr(), gc.Not(gc.Equals), listener.Addr().String()) // Dial the proxy and check that we see the text echoed correctly. conn, err := net.Dial("tcp", p.Addr()) c.Assert(err, gc.IsNil) defer conn.Close() assertEcho(c, conn) // Close the connection and check that we see // the connection closed for read. conn.(*net.TCPConn).CloseWrite() assertEOF(c, conn) // Make another connection and close the proxy, // which should close down the proxy and cause us // to get an error. conn, err = net.Dial("tcp", p.Addr()) c.Assert(err, gc.IsNil) defer conn.Close() p.Close() assertEOF(c, conn) // Make sure that we cannot dial the proxy address either. conn, err = net.Dial("tcp", p.Addr()) c.Assert(err, gc.ErrorMatches, ".*connection refused") listener.Close() // Make sure that all our connections have gone away too. wg.Wait() } func (*tcpProxySuite) TestCloseConns(c *gc.C) { var wg sync.WaitGroup listener, err := net.Listen("tcp", "127.0.0.1:0") c.Assert(err, gc.IsNil) defer listener.Close() wg.Add(1) go tcpEcho(&wg, listener) p := testing.NewTCPProxy(c, listener.Addr().String()) c.Assert(p.Addr(), gc.Not(gc.Equals), listener.Addr().String()) // Make a couple of connections through the proxy // and test that they work. conn1, err := net.Dial("tcp", p.Addr()) c.Assert(err, gc.IsNil) defer conn1.Close() assertEcho(c, conn1) conn2, err := net.Dial("tcp", p.Addr()) c.Assert(err, gc.IsNil) defer conn1.Close() assertEcho(c, conn1) p.CloseConns() // Assert that both the connections have been broken. assertEOF(c, conn1) assertEOF(c, conn2) // Check that we can still make a connection. conn3, err := net.Dial("tcp", p.Addr()) c.Assert(err, gc.IsNil) defer conn3.Close() assertEcho(c, conn3) // Close the proxy and check that the last connection goes. p.Close() assertEOF(c, conn3) listener.Close() // Make sure that all our connections have gone away too. wg.Wait() } func (*tcpProxySuite) TestPauseConns(c *gc.C) { var wg sync.WaitGroup listener, err := net.Listen("tcp", "127.0.0.1:0") c.Assert(err, gc.IsNil) defer listener.Close() wg.Add(1) go tcpEcho(&wg, listener) p := testing.NewTCPProxy(c, listener.Addr().String()) c.Assert(p.Addr(), gc.Not(gc.Equals), listener.Addr().String()) // Make a connection through the proxy // and test that it works. conn, err := net.Dial("tcp", p.Addr()) c.Assert(err, gc.IsNil) defer conn.Close() assertEcho(c, conn) p.PauseConns() msg := "hello, world\n" n, err := fmt.Fprint(conn, msg) c.Assert(err, gc.IsNil) c.Assert(n, gc.Equals, len(msg)) assertReadTimeout(c, conn) p.ResumeConns() buf := make([]byte, n) n, err = conn.Read(buf) c.Assert(err, gc.IsNil) c.Assert(n, gc.Equals, len(msg)) c.Assert(string(buf), gc.Equals, msg) } // tcpEcho listens on the given listener for TCP connections, // writes all traffic received back to the sender, and calls // wg.Done when all its goroutines have completed. func tcpEcho(wg *sync.WaitGroup, listener net.Listener) { defer wg.Done() for { conn, err := listener.Accept() if err != nil { return } wg.Add(1) go func() { defer wg.Done() defer conn.Close() // Echo anything that was written. io.Copy(conn, conn) }() } } func assertEcho(c *gc.C, conn net.Conn) { txt := "hello, world\n" fmt.Fprint(conn, txt) buf := make([]byte, len(txt)) n, err := io.ReadFull(conn, buf) c.Assert(err, gc.IsNil) c.Assert(string(buf[0:n]), gc.Equals, txt) } func assertEOF(c *gc.C, r io.Reader) { n, err := r.Read(make([]byte, 1)) c.Assert(err, gc.Equals, io.EOF) c.Assert(n, gc.Equals, 0) } func assertReadTimeout(c *gc.C, conn net.Conn) { err := conn.SetReadDeadline(time.Now().Add(500 * time.Millisecond)) c.Assert(err, gc.IsNil) defer conn.SetReadDeadline(time.Time{}) buf := make([]byte, 1) n, err := conn.Read(buf) c.Assert(n, gc.Equals, 0) nerr, ok := err.(net.Error) c.Assert(ok, gc.Equals, true) c.Assert(nerr.Timeout(), gc.Equals, true) }