Browse code

Replace gotestyourself by gotest.tools

github.com/gotestyourself/gotestyourself moved to gotest.tools with
version 2.0.0. Moving to that one, bumping it to v2.1.0.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>

Vincent Demeester authored on 2018/06/09 01:09:51
Showing 45 changed files
... ...
@@ -19,7 +19,7 @@ github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
19 19
 github.com/docker/go-connections 7beb39f0b969b075d1325fecb092faf27fd357b6
20 20
 golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756
21 21
 github.com/pmezard/go-difflib v1.0.0
22
-github.com/gotestyourself/gotestyourself cf3a5ab914a2efa8bc838d09f5918c1d44d029
22
+gotest.tools v2.1.0
23 23
 github.com/google/go-cmp v0.2.0
24 24
 
25 25
 github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5
26 26
deleted file mode 100644
... ...
@@ -1,202 +0,0 @@
1
-
2
-                                 Apache License
3
-                           Version 2.0, January 2004
4
-                        http://www.apache.org/licenses/
5
-
6
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
-
8
-   1. Definitions.
9
-
10
-      "License" shall mean the terms and conditions for use, reproduction,
11
-      and distribution as defined by Sections 1 through 9 of this document.
12
-
13
-      "Licensor" shall mean the copyright owner or entity authorized by
14
-      the copyright owner that is granting the License.
15
-
16
-      "Legal Entity" shall mean the union of the acting entity and all
17
-      other entities that control, are controlled by, or are under common
18
-      control with that entity. For the purposes of this definition,
19
-      "control" means (i) the power, direct or indirect, to cause the
20
-      direction or management of such entity, whether by contract or
21
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
-      outstanding shares, or (iii) beneficial ownership of such entity.
23
-
24
-      "You" (or "Your") shall mean an individual or Legal Entity
25
-      exercising permissions granted by this License.
26
-
27
-      "Source" form shall mean the preferred form for making modifications,
28
-      including but not limited to software source code, documentation
29
-      source, and configuration files.
30
-
31
-      "Object" form shall mean any form resulting from mechanical
32
-      transformation or translation of a Source form, including but
33
-      not limited to compiled object code, generated documentation,
34
-      and conversions to other media types.
35
-
36
-      "Work" shall mean the work of authorship, whether in Source or
37
-      Object form, made available under the License, as indicated by a
38
-      copyright notice that is included in or attached to the work
39
-      (an example is provided in the Appendix below).
40
-
41
-      "Derivative Works" shall mean any work, whether in Source or Object
42
-      form, that is based on (or derived from) the Work and for which the
43
-      editorial revisions, annotations, elaborations, or other modifications
44
-      represent, as a whole, an original work of authorship. For the purposes
45
-      of this License, Derivative Works shall not include works that remain
46
-      separable from, or merely link (or bind by name) to the interfaces of,
47
-      the Work and Derivative Works thereof.
48
-
49
-      "Contribution" shall mean any work of authorship, including
50
-      the original version of the Work and any modifications or additions
51
-      to that Work or Derivative Works thereof, that is intentionally
52
-      submitted to Licensor for inclusion in the Work by the copyright owner
53
-      or by an individual or Legal Entity authorized to submit on behalf of
54
-      the copyright owner. For the purposes of this definition, "submitted"
55
-      means any form of electronic, verbal, or written communication sent
56
-      to the Licensor or its representatives, including but not limited to
57
-      communication on electronic mailing lists, source code control systems,
58
-      and issue tracking systems that are managed by, or on behalf of, the
59
-      Licensor for the purpose of discussing and improving the Work, but
60
-      excluding communication that is conspicuously marked or otherwise
61
-      designated in writing by the copyright owner as "Not a Contribution."
62
-
63
-      "Contributor" shall mean Licensor and any individual or Legal Entity
64
-      on behalf of whom a Contribution has been received by Licensor and
65
-      subsequently incorporated within the Work.
66
-
67
-   2. Grant of Copyright License. Subject to the terms and conditions of
68
-      this License, each Contributor hereby grants to You a perpetual,
69
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
-      copyright license to reproduce, prepare Derivative Works of,
71
-      publicly display, publicly perform, sublicense, and distribute the
72
-      Work and such Derivative Works in Source or Object form.
73
-
74
-   3. Grant of Patent License. Subject to the terms and conditions of
75
-      this License, each Contributor hereby grants to You a perpetual,
76
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
-      (except as stated in this section) patent license to make, have made,
78
-      use, offer to sell, sell, import, and otherwise transfer the Work,
79
-      where such license applies only to those patent claims licensable
80
-      by such Contributor that are necessarily infringed by their
81
-      Contribution(s) alone or by combination of their Contribution(s)
82
-      with the Work to which such Contribution(s) was submitted. If You
83
-      institute patent litigation against any entity (including a
84
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
85
-      or a Contribution incorporated within the Work constitutes direct
86
-      or contributory patent infringement, then any patent licenses
87
-      granted to You under this License for that Work shall terminate
88
-      as of the date such litigation is filed.
89
-
90
-   4. Redistribution. You may reproduce and distribute copies of the
91
-      Work or Derivative Works thereof in any medium, with or without
92
-      modifications, and in Source or Object form, provided that You
93
-      meet the following conditions:
94
-
95
-      (a) You must give any other recipients of the Work or
96
-          Derivative Works a copy of this License; and
97
-
98
-      (b) You must cause any modified files to carry prominent notices
99
-          stating that You changed the files; and
100
-
101
-      (c) You must retain, in the Source form of any Derivative Works
102
-          that You distribute, all copyright, patent, trademark, and
103
-          attribution notices from the Source form of the Work,
104
-          excluding those notices that do not pertain to any part of
105
-          the Derivative Works; and
106
-
107
-      (d) If the Work includes a "NOTICE" text file as part of its
108
-          distribution, then any Derivative Works that You distribute must
109
-          include a readable copy of the attribution notices contained
110
-          within such NOTICE file, excluding those notices that do not
111
-          pertain to any part of the Derivative Works, in at least one
112
-          of the following places: within a NOTICE text file distributed
113
-          as part of the Derivative Works; within the Source form or
114
-          documentation, if provided along with the Derivative Works; or,
115
-          within a display generated by the Derivative Works, if and
116
-          wherever such third-party notices normally appear. The contents
117
-          of the NOTICE file are for informational purposes only and
118
-          do not modify the License. You may add Your own attribution
119
-          notices within Derivative Works that You distribute, alongside
120
-          or as an addendum to the NOTICE text from the Work, provided
121
-          that such additional attribution notices cannot be construed
122
-          as modifying the License.
123
-
124
-      You may add Your own copyright statement to Your modifications and
125
-      may provide additional or different license terms and conditions
126
-      for use, reproduction, or distribution of Your modifications, or
127
-      for any such Derivative Works as a whole, provided Your use,
128
-      reproduction, and distribution of the Work otherwise complies with
129
-      the conditions stated in this License.
130
-
131
-   5. Submission of Contributions. Unless You explicitly state otherwise,
132
-      any Contribution intentionally submitted for inclusion in the Work
133
-      by You to the Licensor shall be under the terms and conditions of
134
-      this License, without any additional terms or conditions.
135
-      Notwithstanding the above, nothing herein shall supersede or modify
136
-      the terms of any separate license agreement you may have executed
137
-      with Licensor regarding such Contributions.
138
-
139
-   6. Trademarks. This License does not grant permission to use the trade
140
-      names, trademarks, service marks, or product names of the Licensor,
141
-      except as required for reasonable and customary use in describing the
142
-      origin of the Work and reproducing the content of the NOTICE file.
143
-
144
-   7. Disclaimer of Warranty. Unless required by applicable law or
145
-      agreed to in writing, Licensor provides the Work (and each
146
-      Contributor provides its Contributions) on an "AS IS" BASIS,
147
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
-      implied, including, without limitation, any warranties or conditions
149
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
-      PARTICULAR PURPOSE. You are solely responsible for determining the
151
-      appropriateness of using or redistributing the Work and assume any
152
-      risks associated with Your exercise of permissions under this License.
153
-
154
-   8. Limitation of Liability. In no event and under no legal theory,
155
-      whether in tort (including negligence), contract, or otherwise,
156
-      unless required by applicable law (such as deliberate and grossly
157
-      negligent acts) or agreed to in writing, shall any Contributor be
158
-      liable to You for damages, including any direct, indirect, special,
159
-      incidental, or consequential damages of any character arising as a
160
-      result of this License or out of the use or inability to use the
161
-      Work (including but not limited to damages for loss of goodwill,
162
-      work stoppage, computer failure or malfunction, or any and all
163
-      other commercial damages or losses), even if such Contributor
164
-      has been advised of the possibility of such damages.
165
-
166
-   9. Accepting Warranty or Additional Liability. While redistributing
167
-      the Work or Derivative Works thereof, You may choose to offer,
168
-      and charge a fee for, acceptance of support, warranty, indemnity,
169
-      or other liability obligations and/or rights consistent with this
170
-      License. However, in accepting such obligations, You may act only
171
-      on Your own behalf and on Your sole responsibility, not on behalf
172
-      of any other Contributor, and only if You agree to indemnify,
173
-      defend, and hold each Contributor harmless for any liability
174
-      incurred by, or claims asserted against, such Contributor by reason
175
-      of your accepting any such warranty or additional liability.
176
-
177
-   END OF TERMS AND CONDITIONS
178
-
179
-   APPENDIX: How to apply the Apache License to your work.
180
-
181
-      To apply the Apache License to your work, attach the following
182
-      boilerplate notice, with the fields enclosed by brackets "[]"
183
-      replaced with your own identifying information. (Don't include
184
-      the brackets!)  The text should be enclosed in the appropriate
185
-      comment syntax for the file format. We also recommend that a
186
-      file or class name and description of purpose be included on the
187
-      same "printed page" as the copyright notice for easier
188
-      identification within third-party archives.
189
-
190
-   Copyright [yyyy] [name of copyright owner]
191
-
192
-   Licensed under the Apache License, Version 2.0 (the "License");
193
-   you may not use this file except in compliance with the License.
194
-   You may obtain a copy of the License at
195
-
196
-       http://www.apache.org/licenses/LICENSE-2.0
197
-
198
-   Unless required by applicable law or agreed to in writing, software
199
-   distributed under the License is distributed on an "AS IS" BASIS,
200
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
-   See the License for the specific language governing permissions and
202
-   limitations under the License.
203 1
deleted file mode 100644
... ...
@@ -1,33 +0,0 @@
1
-# Go Test Yourself
2
-
3
-A collection of packages compatible with `go test` to support common testing
4
-patterns.
5
-
6
-[![GoDoc](https://godoc.org/github.com/gotestyourself/gotestyourself?status.svg)](https://godoc.org/github.com/gotestyourself/gotestyourself)
7
-[![CircleCI](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master.svg?style=shield)](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master)
8
-[![Go Reportcard](https://goreportcard.com/badge/github.com/gotestyourself/gotestyourself)](https://goreportcard.com/report/github.com/gotestyourself/gotestyourself)
9
-
10
-
11
-## Packages
12
-
13
-* [assert](http://godoc.org/github.com/gotestyourself/gotestyourself/assert) -
14
-  compare values and fail the test when the comparison fails
15
-* [env](http://godoc.org/github.com/gotestyourself/gotestyourself/env) -
16
-  test code that uses environment variables
17
-* [fs](http://godoc.org/github.com/gotestyourself/gotestyourself/fs) -
18
-  create test files and directories
19
-* [golden](http://godoc.org/github.com/gotestyourself/gotestyourself/golden) -
20
-  compare large multi-line strings
21
-* [icmd](http://godoc.org/github.com/gotestyourself/gotestyourself/icmd) -
22
-  execute binaries and test the output
23
-* [poll](http://godoc.org/github.com/gotestyourself/gotestyourself/poll) -
24
-  test asynchronous code by polling until a desired state is reached
25
-* [skip](http://godoc.org/github.com/gotestyourself/gotestyourself/skip) -
26
-  skip tests based on conditions
27
-* [testsum](http://godoc.org/github.com/gotestyourself/gotestyourself/testsum) -
28
-  a program to summarize `go test` output and test failures
29
-
30
-## Related
31
-
32
-* [maxbrunsfeld/counterfeiter](https://github.com/maxbrunsfeld/counterfeiter) - generate fakes for interfaces
33
-* [jonboulle/clockwork](https://github.com/jonboulle/clockwork) - a fake clock for testing code that uses `time`
34 1
deleted file mode 100644
... ...
@@ -1,289 +0,0 @@
1
-/*Package assert provides assertions for comparing expected values to actual
2
-values. When an assertion fails a helpful error message is printed.
3
-
4
-Assert and Check
5
-
6
-Assert() and Check() both accept a Comparison, and fail the test when the
7
-comparison fails. The one difference is that Assert() will end the test execution
8
-immediately (using t.FailNow()) whereas Check() will fail the test (using t.Fail()),
9
-return the value of the comparison, then proceed with the rest of the test case.
10
-
11
-Example Usage
12
-
13
-The example below shows assert used with some common types.
14
-
15
-
16
-	import (
17
-	    "testing"
18
-
19
-	    "github.com/gotestyourself/gotestyourself/assert"
20
-	    is "github.com/gotestyourself/gotestyourself/assert/cmp"
21
-	)
22
-
23
-	func TestEverything(t *testing.T) {
24
-	    // booleans
25
-	    assert.Assert(t, ok)
26
-	    assert.Assert(t, !missing)
27
-
28
-	    // primitives
29
-	    assert.Equal(t, count, 1)
30
-	    assert.Equal(t, msg, "the message")
31
-	    assert.Assert(t, total != 10) // NotEqual
32
-
33
-	    // errors
34
-	    assert.NilError(t, closer.Close())
35
-	    assert.Error(t, err, "the exact error message")
36
-	    assert.ErrorContains(t, err, "includes this")
37
-	    assert.ErrorType(t, err, os.IsNotExist)
38
-
39
-	    // complex types
40
-	    assert.DeepEqual(t, result, myStruct{Name: "title"})
41
-	    assert.Assert(t, is.Len(items, 3))
42
-	    assert.Assert(t, len(sequence) != 0) // NotEmpty
43
-	    assert.Assert(t, is.Contains(mapping, "key"))
44
-
45
-	    // pointers and interface
46
-	    assert.Assert(t, is.Nil(ref))
47
-	    assert.Assert(t, ref != nil) // NotNil
48
-	}
49
-
50
-Comparisons
51
-
52
-https://godoc.org/github.com/gotestyourself/gotestyourself/assert/cmp provides
53
-many common comparisons. Additional comparisons can be written to compare
54
-values in other ways. See the example Assert (CustomComparison).
55
-
56
-*/
57
-package assert
58
-
59
-import (
60
-	"fmt"
61
-	"go/ast"
62
-	"go/token"
63
-
64
-	gocmp "github.com/google/go-cmp/cmp"
65
-	"github.com/gotestyourself/gotestyourself/assert/cmp"
66
-	"github.com/gotestyourself/gotestyourself/internal/format"
67
-	"github.com/gotestyourself/gotestyourself/internal/source"
68
-)
69
-
70
-// BoolOrComparison can be a bool, or cmp.Comparison. See Assert() for usage.
71
-type BoolOrComparison interface{}
72
-
73
-// TestingT is the subset of testing.T used by the assert package.
74
-type TestingT interface {
75
-	FailNow()
76
-	Fail()
77
-	Log(args ...interface{})
78
-}
79
-
80
-type helperT interface {
81
-	Helper()
82
-}
83
-
84
-const failureMessage = "assertion failed: "
85
-
86
-// nolint: gocyclo
87
-func assert(
88
-	t TestingT,
89
-	failer func(),
90
-	argSelector argSelector,
91
-	comparison BoolOrComparison,
92
-	msgAndArgs ...interface{},
93
-) bool {
94
-	if ht, ok := t.(helperT); ok {
95
-		ht.Helper()
96
-	}
97
-	var success bool
98
-	switch check := comparison.(type) {
99
-	case bool:
100
-		if check {
101
-			return true
102
-		}
103
-		logFailureFromBool(t, msgAndArgs...)
104
-
105
-	// Undocumented legacy comparison without Result type
106
-	case func() (success bool, message string):
107
-		success = runCompareFunc(t, check, msgAndArgs...)
108
-
109
-	case nil:
110
-		return true
111
-
112
-	case error:
113
-		msg := "error is not nil: "
114
-		t.Log(format.WithCustomMessage(failureMessage+msg+check.Error(), msgAndArgs...))
115
-
116
-	case cmp.Comparison:
117
-		success = runComparison(t, argSelector, check, msgAndArgs...)
118
-
119
-	case func() cmp.Result:
120
-		success = runComparison(t, argSelector, check, msgAndArgs...)
121
-
122
-	default:
123
-		t.Log(fmt.Sprintf("invalid Comparison: %v (%T)", check, check))
124
-	}
125
-
126
-	if success {
127
-		return true
128
-	}
129
-	failer()
130
-	return false
131
-}
132
-
133
-func runCompareFunc(
134
-	t TestingT,
135
-	f func() (success bool, message string),
136
-	msgAndArgs ...interface{},
137
-) bool {
138
-	if ht, ok := t.(helperT); ok {
139
-		ht.Helper()
140
-	}
141
-	if success, message := f(); !success {
142
-		t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...))
143
-		return false
144
-	}
145
-	return true
146
-}
147
-
148
-func logFailureFromBool(t TestingT, msgAndArgs ...interface{}) {
149
-	if ht, ok := t.(helperT); ok {
150
-		ht.Helper()
151
-	}
152
-	const stackIndex = 3 // Assert()/Check(), assert(), formatFailureFromBool()
153
-	const comparisonArgPos = 1
154
-	args, err := source.CallExprArgs(stackIndex)
155
-	if err != nil {
156
-		t.Log(err.Error())
157
-		return
158
-	}
159
-
160
-	msg, err := boolFailureMessage(args[comparisonArgPos])
161
-	if err != nil {
162
-		t.Log(err.Error())
163
-		msg = "expression is false"
164
-	}
165
-
166
-	t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...))
167
-}
168
-
169
-func boolFailureMessage(expr ast.Expr) (string, error) {
170
-	if binaryExpr, ok := expr.(*ast.BinaryExpr); ok && binaryExpr.Op == token.NEQ {
171
-		x, err := source.FormatNode(binaryExpr.X)
172
-		if err != nil {
173
-			return "", err
174
-		}
175
-		y, err := source.FormatNode(binaryExpr.Y)
176
-		if err != nil {
177
-			return "", err
178
-		}
179
-		return x + " is " + y, nil
180
-	}
181
-
182
-	if unaryExpr, ok := expr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.NOT {
183
-		x, err := source.FormatNode(unaryExpr.X)
184
-		if err != nil {
185
-			return "", err
186
-		}
187
-		return x + " is true", nil
188
-	}
189
-
190
-	formatted, err := source.FormatNode(expr)
191
-	if err != nil {
192
-		return "", err
193
-	}
194
-	return "expression is false: " + formatted, nil
195
-}
196
-
197
-// Assert performs a comparison. If the comparison fails the test is marked as
198
-// failed, a failure message is logged, and execution is stopped immediately.
199
-//
200
-// The comparison argument may be one of three types: bool, cmp.Comparison or
201
-// error.
202
-// When called with a bool the failure message will contain the literal source
203
-// code of the expression.
204
-// When called with a cmp.Comparison the comparison is responsible for producing
205
-// a helpful failure message.
206
-// When called with an error a nil value is considered success. A non-nil error
207
-// is a failure, and Error() is used as the failure message.
208
-func Assert(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) {
209
-	if ht, ok := t.(helperT); ok {
210
-		ht.Helper()
211
-	}
212
-	assert(t, t.FailNow, argsFromComparisonCall, comparison, msgAndArgs...)
213
-}
214
-
215
-// Check performs a comparison. If the comparison fails the test is marked as
216
-// failed, a failure message is logged, and Check returns false. Otherwise returns
217
-// true.
218
-//
219
-// See Assert for details about the comparison arg and failure messages.
220
-func Check(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) bool {
221
-	if ht, ok := t.(helperT); ok {
222
-		ht.Helper()
223
-	}
224
-	return assert(t, t.Fail, argsFromComparisonCall, comparison, msgAndArgs...)
225
-}
226
-
227
-// NilError fails the test immediately if err is not nil.
228
-// This is equivalent to Assert(t, err)
229
-func NilError(t TestingT, err error, msgAndArgs ...interface{}) {
230
-	if ht, ok := t.(helperT); ok {
231
-		ht.Helper()
232
-	}
233
-	assert(t, t.FailNow, argsAfterT, err, msgAndArgs...)
234
-}
235
-
236
-// Equal uses the == operator to assert two values are equal and fails the test
237
-// if they are not equal. This is equivalent to Assert(t, cmp.Equal(x, y)).
238
-func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) {
239
-	if ht, ok := t.(helperT); ok {
240
-		ht.Helper()
241
-	}
242
-	assert(t, t.FailNow, argsAfterT, cmp.Equal(x, y), msgAndArgs...)
243
-}
244
-
245
-// DeepEqual uses https://github.com/google/go-cmp/cmp to assert two values
246
-// are equal and fails the test if they are not equal.
247
-// This is equivalent to Assert(t, cmp.DeepEqual(x, y)).
248
-func DeepEqual(t TestingT, x, y interface{}, opts ...gocmp.Option) {
249
-	if ht, ok := t.(helperT); ok {
250
-		ht.Helper()
251
-	}
252
-	assert(t, t.FailNow, argsAfterT, cmp.DeepEqual(x, y, opts...))
253
-}
254
-
255
-// Error fails the test if err is nil, or the error message is not the expected
256
-// message.
257
-// Equivalent to Assert(t, cmp.Error(err, message)).
258
-func Error(t TestingT, err error, message string, msgAndArgs ...interface{}) {
259
-	if ht, ok := t.(helperT); ok {
260
-		ht.Helper()
261
-	}
262
-	assert(t, t.FailNow, argsAfterT, cmp.Error(err, message), msgAndArgs...)
263
-}
264
-
265
-// ErrorContains fails the test if err is nil, or the error message does not
266
-// contain the expected substring.
267
-// Equivalent to Assert(t, cmp.ErrorContains(err, substring)).
268
-func ErrorContains(t TestingT, err error, substring string, msgAndArgs ...interface{}) {
269
-	if ht, ok := t.(helperT); ok {
270
-		ht.Helper()
271
-	}
272
-	assert(t, t.FailNow, argsAfterT, cmp.ErrorContains(err, substring), msgAndArgs...)
273
-}
274
-
275
-// ErrorType fails the test if err is nil, or err is not the expected type.
276
-//
277
-// Expected can be one of:
278
-// a func(error) bool which returns true if the error is the expected type,
279
-// an instance of a struct of the expected type,
280
-// a pointer to an interface the error is expected to implement,
281
-// a reflect.Type of the expected struct or interface.
282
-//
283
-// Equivalent to Assert(t, cmp.ErrorType(err, expected)).
284
-func ErrorType(t TestingT, err error, expected interface{}, msgAndArgs ...interface{}) {
285
-	if ht, ok := t.(helperT); ok {
286
-		ht.Helper()
287
-	}
288
-	assert(t, t.FailNow, argsAfterT, cmp.ErrorType(err, expected), msgAndArgs...)
289
-}
290 1
deleted file mode 100644
... ...
@@ -1,310 +0,0 @@
1
-/*Package cmp provides Comparisons for Assert and Check*/
2
-package cmp
3
-
4
-import (
5
-	"fmt"
6
-	"reflect"
7
-	"strings"
8
-
9
-	"github.com/google/go-cmp/cmp"
10
-	"github.com/pmezard/go-difflib/difflib"
11
-)
12
-
13
-// Comparison is a function which compares values and returns ResultSuccess if
14
-// the actual value matches the expected value. If the values do not match the
15
-// Result will contain a message about why it failed.
16
-type Comparison func() Result
17
-
18
-// DeepEqual compares two values using https://godoc.org/github.com/google/go-cmp/cmp
19
-// and succeeds if the values are equal.
20
-//
21
-// The comparison can be customized using comparison Options.
22
-func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison {
23
-	return func() (result Result) {
24
-		defer func() {
25
-			if panicmsg, handled := handleCmpPanic(recover()); handled {
26
-				result = ResultFailure(panicmsg)
27
-			}
28
-		}()
29
-		diff := cmp.Diff(x, y, opts...)
30
-		return toResult(diff == "", "\n"+diff)
31
-	}
32
-}
33
-
34
-func handleCmpPanic(r interface{}) (string, bool) {
35
-	if r == nil {
36
-		return "", false
37
-	}
38
-	panicmsg, ok := r.(string)
39
-	if !ok {
40
-		panic(r)
41
-	}
42
-	switch {
43
-	case strings.HasPrefix(panicmsg, "cannot handle unexported field"):
44
-		return panicmsg, true
45
-	}
46
-	panic(r)
47
-}
48
-
49
-func toResult(success bool, msg string) Result {
50
-	if success {
51
-		return ResultSuccess
52
-	}
53
-	return ResultFailure(msg)
54
-}
55
-
56
-// Equal succeeds if x == y.
57
-func Equal(x, y interface{}) Comparison {
58
-	return func() Result {
59
-		switch {
60
-		case x == y:
61
-			return ResultSuccess
62
-		case isMultiLineStringCompare(x, y):
63
-			return multiLineStringDiffResult(x.(string), y.(string))
64
-		}
65
-		return ResultFailureTemplate(`
66
-			{{- .Data.x}} (
67
-				{{- with callArg 0 }}{{ formatNode . }} {{end -}}
68
-				{{- printf "%T" .Data.x -}}
69
-			) != {{ .Data.y}} (
70
-				{{- with callArg 1 }}{{ formatNode . }} {{end -}}
71
-				{{- printf "%T" .Data.y -}}
72
-			)`,
73
-			map[string]interface{}{"x": x, "y": y})
74
-	}
75
-}
76
-
77
-func isMultiLineStringCompare(x, y interface{}) bool {
78
-	strX, ok := x.(string)
79
-	if !ok {
80
-		return false
81
-	}
82
-	strY, ok := y.(string)
83
-	if !ok {
84
-		return false
85
-	}
86
-	return strings.Contains(strX, "\n") || strings.Contains(strY, "\n")
87
-}
88
-
89
-func multiLineStringDiffResult(x, y string) Result {
90
-	diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
91
-		A:       difflib.SplitLines(x),
92
-		B:       difflib.SplitLines(y),
93
-		Context: 3,
94
-	})
95
-	if err != nil {
96
-		return ResultFailure(fmt.Sprintf("failed to diff: %s", err))
97
-	}
98
-	return ResultFailureTemplate(`
99
-+++ {{ with callArg 1 }}{{ formatNode . }}{{else}}→{{end}}
100
-{{ .Data.diff }}`,
101
-		map[string]interface{}{"diff": diff})
102
-}
103
-
104
-// Len succeeds if the sequence has the expected length.
105
-func Len(seq interface{}, expected int) Comparison {
106
-	return func() (result Result) {
107
-		defer func() {
108
-			if e := recover(); e != nil {
109
-				result = ResultFailure(fmt.Sprintf("type %T does not have a length", seq))
110
-			}
111
-		}()
112
-		value := reflect.ValueOf(seq)
113
-		length := value.Len()
114
-		if length == expected {
115
-			return ResultSuccess
116
-		}
117
-		msg := fmt.Sprintf("expected %s (length %d) to have length %d", seq, length, expected)
118
-		return ResultFailure(msg)
119
-	}
120
-}
121
-
122
-// Contains succeeds if item is in collection. Collection may be a string, map,
123
-// slice, or array.
124
-//
125
-// If collection is a string, item must also be a string, and is compared using
126
-// strings.Contains().
127
-// If collection is a Map, contains will succeed if item is a key in the map.
128
-// If collection is a slice or array, item is compared to each item in the
129
-// sequence using reflect.DeepEqual().
130
-func Contains(collection interface{}, item interface{}) Comparison {
131
-	return func() Result {
132
-		colValue := reflect.ValueOf(collection)
133
-		if !colValue.IsValid() {
134
-			return ResultFailure(fmt.Sprintf("nil does not contain items"))
135
-		}
136
-		msg := fmt.Sprintf("%v does not contain %v", collection, item)
137
-
138
-		itemValue := reflect.ValueOf(item)
139
-		switch colValue.Type().Kind() {
140
-		case reflect.String:
141
-			if itemValue.Type().Kind() != reflect.String {
142
-				return ResultFailure("string may only contain strings")
143
-			}
144
-			return toResult(
145
-				strings.Contains(colValue.String(), itemValue.String()),
146
-				fmt.Sprintf("string %q does not contain %q", collection, item))
147
-
148
-		case reflect.Map:
149
-			if itemValue.Type() != colValue.Type().Key() {
150
-				return ResultFailure(fmt.Sprintf(
151
-					"%v can not contain a %v key", colValue.Type(), itemValue.Type()))
152
-			}
153
-			return toResult(colValue.MapIndex(itemValue).IsValid(), msg)
154
-
155
-		case reflect.Slice, reflect.Array:
156
-			for i := 0; i < colValue.Len(); i++ {
157
-				if reflect.DeepEqual(colValue.Index(i).Interface(), item) {
158
-					return ResultSuccess
159
-				}
160
-			}
161
-			return ResultFailure(msg)
162
-		default:
163
-			return ResultFailure(fmt.Sprintf("type %T does not contain items", collection))
164
-		}
165
-	}
166
-}
167
-
168
-// Panics succeeds if f() panics.
169
-func Panics(f func()) Comparison {
170
-	return func() (result Result) {
171
-		defer func() {
172
-			if err := recover(); err != nil {
173
-				result = ResultSuccess
174
-			}
175
-		}()
176
-		f()
177
-		return ResultFailure("did not panic")
178
-	}
179
-}
180
-
181
-// Error succeeds if err is a non-nil error, and the error message equals the
182
-// expected message.
183
-func Error(err error, message string) Comparison {
184
-	return func() Result {
185
-		switch {
186
-		case err == nil:
187
-			return ResultFailure("expected an error, got nil")
188
-		case err.Error() != message:
189
-			return ResultFailure(fmt.Sprintf(
190
-				"expected error %q, got %+v", message, err))
191
-		}
192
-		return ResultSuccess
193
-	}
194
-}
195
-
196
-// ErrorContains succeeds if err is a non-nil error, and the error message contains
197
-// the expected substring.
198
-func ErrorContains(err error, substring string) Comparison {
199
-	return func() Result {
200
-		switch {
201
-		case err == nil:
202
-			return ResultFailure("expected an error, got nil")
203
-		case !strings.Contains(err.Error(), substring):
204
-			return ResultFailure(fmt.Sprintf(
205
-				"expected error to contain %q, got %+v", substring, err))
206
-		}
207
-		return ResultSuccess
208
-	}
209
-}
210
-
211
-// Nil succeeds if obj is a nil interface, pointer, or function.
212
-//
213
-// Use NilError() for comparing errors. Use Len(obj, 0) for comparing slices,
214
-// maps, and channels.
215
-func Nil(obj interface{}) Comparison {
216
-	msgFunc := func(value reflect.Value) string {
217
-		return fmt.Sprintf("%v (type %s) is not nil", reflect.Indirect(value), value.Type())
218
-	}
219
-	return isNil(obj, msgFunc)
220
-}
221
-
222
-func isNil(obj interface{}, msgFunc func(reflect.Value) string) Comparison {
223
-	return func() Result {
224
-		if obj == nil {
225
-			return ResultSuccess
226
-		}
227
-		value := reflect.ValueOf(obj)
228
-		kind := value.Type().Kind()
229
-		if kind >= reflect.Chan && kind <= reflect.Slice {
230
-			if value.IsNil() {
231
-				return ResultSuccess
232
-			}
233
-			return ResultFailure(msgFunc(value))
234
-		}
235
-
236
-		return ResultFailure(fmt.Sprintf("%v (type %s) can not be nil", value, value.Type()))
237
-	}
238
-}
239
-
240
-// ErrorType succeeds if err is not nil and is of the expected type.
241
-//
242
-// Expected can be one of:
243
-// a func(error) bool which returns true if the error is the expected type,
244
-// an instance of a struct of the expected type,
245
-// a pointer to an interface the error is expected to implement,
246
-// a reflect.Type of the expected struct or interface.
247
-func ErrorType(err error, expected interface{}) Comparison {
248
-	return func() Result {
249
-		switch expectedType := expected.(type) {
250
-		case func(error) bool:
251
-			return cmpErrorTypeFunc(err, expectedType)
252
-		case reflect.Type:
253
-			if expectedType.Kind() == reflect.Interface {
254
-				return cmpErrorTypeImplementsType(err, expectedType)
255
-			}
256
-			return cmpErrorTypeEqualType(err, expectedType)
257
-		case nil:
258
-			return ResultFailure(fmt.Sprintf("invalid type for expected: nil"))
259
-		}
260
-
261
-		expectedType := reflect.TypeOf(expected)
262
-		switch {
263
-		case expectedType.Kind() == reflect.Struct:
264
-			return cmpErrorTypeEqualType(err, expectedType)
265
-		case isPtrToInterface(expectedType):
266
-			return cmpErrorTypeImplementsType(err, expectedType.Elem())
267
-		}
268
-		return ResultFailure(fmt.Sprintf("invalid type for expected: %T", expected))
269
-	}
270
-}
271
-
272
-func cmpErrorTypeFunc(err error, f func(error) bool) Result {
273
-	if f(err) {
274
-		return ResultSuccess
275
-	}
276
-	actual := "nil"
277
-	if err != nil {
278
-		actual = fmt.Sprintf("%s (%T)", err, err)
279
-	}
280
-	return ResultFailureTemplate(`error is {{ .Data.actual }}
281
-		{{- with callArg 1 }}, not {{ formatNode . }}{{end -}}`,
282
-		map[string]interface{}{"actual": actual})
283
-}
284
-
285
-func cmpErrorTypeEqualType(err error, expectedType reflect.Type) Result {
286
-	if err == nil {
287
-		return ResultFailure(fmt.Sprintf("error is nil, not %s", expectedType))
288
-	}
289
-	errValue := reflect.ValueOf(err)
290
-	if errValue.Type() == expectedType {
291
-		return ResultSuccess
292
-	}
293
-	return ResultFailure(fmt.Sprintf("error is %s (%T), not %s", err, err, expectedType))
294
-}
295
-
296
-func cmpErrorTypeImplementsType(err error, expectedType reflect.Type) Result {
297
-	if err == nil {
298
-		return ResultFailure(fmt.Sprintf("error is nil, not %s", expectedType))
299
-	}
300
-	errValue := reflect.ValueOf(err)
301
-	if errValue.Type().Implements(expectedType) {
302
-		return ResultSuccess
303
-	}
304
-	return ResultFailure(fmt.Sprintf("error is %s (%T), not %s", err, err, expectedType))
305
-}
306
-
307
-func isPtrToInterface(typ reflect.Type) bool {
308
-	return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Interface
309
-}
310 1
deleted file mode 100644
... ...
@@ -1,94 +0,0 @@
1
-package cmp
2
-
3
-import (
4
-	"bytes"
5
-	"fmt"
6
-	"go/ast"
7
-	"text/template"
8
-
9
-	"github.com/gotestyourself/gotestyourself/internal/source"
10
-)
11
-
12
-// Result of a Comparison.
13
-type Result interface {
14
-	Success() bool
15
-}
16
-
17
-type result struct {
18
-	success bool
19
-	message string
20
-}
21
-
22
-func (r result) Success() bool {
23
-	return r.success
24
-}
25
-
26
-func (r result) FailureMessage() string {
27
-	return r.message
28
-}
29
-
30
-// ResultSuccess is a constant which is returned by a ComparisonWithResult to
31
-// indicate success.
32
-var ResultSuccess = result{success: true}
33
-
34
-// ResultFailure returns a failed Result with a failure message.
35
-func ResultFailure(message string) Result {
36
-	return result{message: message}
37
-}
38
-
39
-// ResultFromError returns ResultSuccess if err is nil. Otherwise ResultFailure
40
-// is returned with the error message as the failure message.
41
-func ResultFromError(err error) Result {
42
-	if err == nil {
43
-		return ResultSuccess
44
-	}
45
-	return ResultFailure(err.Error())
46
-}
47
-
48
-type templatedResult struct {
49
-	success  bool
50
-	template string
51
-	data     map[string]interface{}
52
-}
53
-
54
-func (r templatedResult) Success() bool {
55
-	return r.success
56
-}
57
-
58
-func (r templatedResult) FailureMessage(args []ast.Expr) string {
59
-	msg, err := renderMessage(r, args)
60
-	if err != nil {
61
-		return fmt.Sprintf("failed to render failure message: %s", err)
62
-	}
63
-	return msg
64
-}
65
-
66
-// ResultFailureTemplate returns a Result with a template string and data which
67
-// can be used to format a failure message. The template may access data from .Data,
68
-// the comparison args with the callArg function, and the formatNode function may
69
-// be used to format the call args.
70
-func ResultFailureTemplate(template string, data map[string]interface{}) Result {
71
-	return templatedResult{template: template, data: data}
72
-}
73
-
74
-func renderMessage(result templatedResult, args []ast.Expr) (string, error) {
75
-	tmpl := template.New("failure").Funcs(template.FuncMap{
76
-		"formatNode": source.FormatNode,
77
-		"callArg": func(index int) ast.Expr {
78
-			if index >= len(args) {
79
-				return nil
80
-			}
81
-			return args[index]
82
-		},
83
-	})
84
-	var err error
85
-	tmpl, err = tmpl.Parse(result.template)
86
-	if err != nil {
87
-		return "", err
88
-	}
89
-	buf := new(bytes.Buffer)
90
-	err = tmpl.Execute(buf, map[string]interface{}{
91
-		"Data": result.data,
92
-	})
93
-	return buf.String(), err
94
-}
95 1
deleted file mode 100644
... ...
@@ -1,107 +0,0 @@
1
-package assert
2
-
3
-import (
4
-	"fmt"
5
-	"go/ast"
6
-
7
-	"github.com/gotestyourself/gotestyourself/assert/cmp"
8
-	"github.com/gotestyourself/gotestyourself/internal/format"
9
-	"github.com/gotestyourself/gotestyourself/internal/source"
10
-)
11
-
12
-func runComparison(
13
-	t TestingT,
14
-	argSelector argSelector,
15
-	f cmp.Comparison,
16
-	msgAndArgs ...interface{},
17
-) bool {
18
-	if ht, ok := t.(helperT); ok {
19
-		ht.Helper()
20
-	}
21
-	result := f()
22
-	if result.Success() {
23
-		return true
24
-	}
25
-
26
-	var message string
27
-	switch typed := result.(type) {
28
-	case resultWithComparisonArgs:
29
-		const stackIndex = 3 // Assert/Check, assert, runComparison
30
-		args, err := source.CallExprArgs(stackIndex)
31
-		if err != nil {
32
-			t.Log(err.Error())
33
-		}
34
-		message = typed.FailureMessage(filterPrintableExpr(argSelector(args)))
35
-	case resultBasic:
36
-		message = typed.FailureMessage()
37
-	default:
38
-		message = fmt.Sprintf("comparison returned invalid Result type: %T", result)
39
-	}
40
-
41
-	t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...))
42
-	return false
43
-}
44
-
45
-type resultWithComparisonArgs interface {
46
-	FailureMessage(args []ast.Expr) string
47
-}
48
-
49
-type resultBasic interface {
50
-	FailureMessage() string
51
-}
52
-
53
-// filterPrintableExpr filters the ast.Expr slice to only include Expr that are
54
-// easy to read when printed and contain relevant information to an assertion.
55
-//
56
-// Ident and SelectorExpr are included because they print nicely and the variable
57
-// names may provide additional context to their values.
58
-// BasicLit and CompositeLit are excluded because their source is equivalent to
59
-// their value, which is already available.
60
-// Other types are ignored for now, but could be added if they are relevant.
61
-func filterPrintableExpr(args []ast.Expr) []ast.Expr {
62
-	result := make([]ast.Expr, len(args))
63
-	for i, arg := range args {
64
-		if isShortPrintableExpr(arg) {
65
-			result[i] = arg
66
-			continue
67
-		}
68
-
69
-		if starExpr, ok := arg.(*ast.StarExpr); ok {
70
-			result[i] = starExpr.X
71
-			continue
72
-		}
73
-		result[i] = nil
74
-	}
75
-	return result
76
-}
77
-
78
-func isShortPrintableExpr(expr ast.Expr) bool {
79
-	switch expr.(type) {
80
-	case *ast.Ident, *ast.SelectorExpr, *ast.IndexExpr, *ast.SliceExpr:
81
-		return true
82
-	case *ast.BinaryExpr, *ast.UnaryExpr:
83
-		return true
84
-	default:
85
-		// CallExpr, ParenExpr, TypeAssertExpr, KeyValueExpr, StarExpr
86
-		return false
87
-	}
88
-}
89
-
90
-type argSelector func([]ast.Expr) []ast.Expr
91
-
92
-func argsAfterT(args []ast.Expr) []ast.Expr {
93
-	if len(args) < 1 {
94
-		return nil
95
-	}
96
-	return args[1:]
97
-}
98
-
99
-func argsFromComparisonCall(args []ast.Expr) []ast.Expr {
100
-	if len(args) < 1 {
101
-		return nil
102
-	}
103
-	if callExpr, ok := args[1].(*ast.CallExpr); ok {
104
-		return callExpr.Args
105
-	}
106
-	return nil
107
-}
108 1
deleted file mode 100644
... ...
@@ -1,98 +0,0 @@
1
-/*Package env provides functions to test code that read environment variables
2
-or the current working directory.
3
-*/
4
-package env
5
-
6
-import (
7
-	"os"
8
-	"strings"
9
-
10
-	"github.com/gotestyourself/gotestyourself/assert"
11
-)
12
-
13
-type helperT interface {
14
-	Helper()
15
-}
16
-
17
-// Patch changes the value of an environment variable, and returns a
18
-// function which will reset the the value of that variable back to the
19
-// previous state.
20
-func Patch(t assert.TestingT, key, value string) func() {
21
-	if ht, ok := t.(helperT); ok {
22
-		ht.Helper()
23
-	}
24
-	oldValue, ok := os.LookupEnv(key)
25
-	assert.NilError(t, os.Setenv(key, value))
26
-	return func() {
27
-		if ht, ok := t.(helperT); ok {
28
-			ht.Helper()
29
-		}
30
-		if !ok {
31
-			assert.NilError(t, os.Unsetenv(key))
32
-			return
33
-		}
34
-		assert.NilError(t, os.Setenv(key, oldValue))
35
-	}
36
-}
37
-
38
-// PatchAll sets the environment to env, and returns a function which will
39
-// reset the environment back to the previous state.
40
-func PatchAll(t assert.TestingT, env map[string]string) func() {
41
-	if ht, ok := t.(helperT); ok {
42
-		ht.Helper()
43
-	}
44
-	oldEnv := os.Environ()
45
-	os.Clearenv()
46
-
47
-	for key, value := range env {
48
-		assert.NilError(t, os.Setenv(key, value), "setenv %s=%s", key, value)
49
-	}
50
-	return func() {
51
-		if ht, ok := t.(helperT); ok {
52
-			ht.Helper()
53
-		}
54
-		os.Clearenv()
55
-		for key, oldVal := range ToMap(oldEnv) {
56
-			assert.NilError(t, os.Setenv(key, oldVal), "setenv %s=%s", key, oldVal)
57
-		}
58
-	}
59
-}
60
-
61
-// ToMap takes a list of strings in the format returned by os.Environ() and
62
-// returns a mapping of keys to values.
63
-func ToMap(env []string) map[string]string {
64
-	result := map[string]string{}
65
-	for _, raw := range env {
66
-		key, value := getParts(raw)
67
-		result[key] = value
68
-	}
69
-	return result
70
-}
71
-
72
-func getParts(raw string) (string, string) {
73
-	// Environment variables on windows can begin with =
74
-	// http://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx
75
-	parts := strings.SplitN(raw[1:], "=", 2)
76
-	key := raw[:1] + parts[0]
77
-	if len(parts) == 1 {
78
-		return key, ""
79
-	}
80
-	return key, parts[1]
81
-}
82
-
83
-// ChangeWorkingDir to the directory, and return a function which restores the
84
-// previous working directory.
85
-func ChangeWorkingDir(t assert.TestingT, dir string) func() {
86
-	if ht, ok := t.(helperT); ok {
87
-		ht.Helper()
88
-	}
89
-	cwd, err := os.Getwd()
90
-	assert.NilError(t, err)
91
-	assert.NilError(t, os.Chdir(dir))
92
-	return func() {
93
-		if ht, ok := t.(helperT); ok {
94
-			ht.Helper()
95
-		}
96
-		assert.NilError(t, os.Chdir(cwd))
97
-	}
98
-}
99 1
deleted file mode 100644
... ...
@@ -1,97 +0,0 @@
1
-/*Package fs provides tools for creating and working with temporary files and
2
-directories.
3
-*/
4
-package fs
5
-
6
-import (
7
-	"io/ioutil"
8
-	"os"
9
-	"path/filepath"
10
-
11
-	"github.com/gotestyourself/gotestyourself/assert"
12
-)
13
-
14
-// Path objects return their filesystem path. Both File and Dir implement Path.
15
-type Path interface {
16
-	Path() string
17
-	Remove()
18
-}
19
-
20
-var (
21
-	_ Path = &Dir{}
22
-	_ Path = &File{}
23
-)
24
-
25
-// File is a temporary file on the filesystem
26
-type File struct {
27
-	path string
28
-}
29
-
30
-type helperT interface {
31
-	Helper()
32
-}
33
-
34
-// NewFile creates a new file in a temporary directory using prefix as part of
35
-// the filename. The PathOps are applied to the before returning the File.
36
-func NewFile(t assert.TestingT, prefix string, ops ...PathOp) *File {
37
-	if ht, ok := t.(helperT); ok {
38
-		ht.Helper()
39
-	}
40
-	tempfile, err := ioutil.TempFile("", prefix+"-")
41
-	assert.NilError(t, err)
42
-	file := &File{path: tempfile.Name()}
43
-	assert.NilError(t, tempfile.Close())
44
-
45
-	for _, op := range ops {
46
-		assert.NilError(t, op(file))
47
-	}
48
-	return file
49
-}
50
-
51
-// Path returns the full path to the file
52
-func (f *File) Path() string {
53
-	return f.path
54
-}
55
-
56
-// Remove the file
57
-func (f *File) Remove() {
58
-	// nolint: errcheck
59
-	os.Remove(f.path)
60
-}
61
-
62
-// Dir is a temporary directory
63
-type Dir struct {
64
-	path string
65
-}
66
-
67
-// NewDir returns a new temporary directory using prefix as part of the directory
68
-// name. The PathOps are applied before returning the Dir.
69
-func NewDir(t assert.TestingT, prefix string, ops ...PathOp) *Dir {
70
-	if ht, ok := t.(helperT); ok {
71
-		ht.Helper()
72
-	}
73
-	path, err := ioutil.TempDir("", prefix+"-")
74
-	assert.NilError(t, err)
75
-	dir := &Dir{path: path}
76
-
77
-	for _, op := range ops {
78
-		assert.NilError(t, op(dir))
79
-	}
80
-	return dir
81
-}
82
-
83
-// Path returns the full path to the directory
84
-func (d *Dir) Path() string {
85
-	return d.path
86
-}
87
-
88
-// Remove the directory
89
-func (d *Dir) Remove() {
90
-	// nolint: errcheck
91
-	os.RemoveAll(d.path)
92
-}
93
-
94
-// Join returns a new path with this directory as the base of the path
95
-func (d *Dir) Join(parts ...string) string {
96
-	return filepath.Join(append([]string{d.Path()}, parts...)...)
97
-}
98 1
deleted file mode 100644
... ...
@@ -1,158 +0,0 @@
1
-package fs
2
-
3
-import (
4
-	"io/ioutil"
5
-	"os"
6
-	"path/filepath"
7
-	"time"
8
-)
9
-
10
-// PathOp is a function which accepts a Path to perform some operation
11
-type PathOp func(path Path) error
12
-
13
-// WithContent writes content to a file at Path
14
-func WithContent(content string) PathOp {
15
-	return func(path Path) error {
16
-		return ioutil.WriteFile(path.Path(), []byte(content), 0644)
17
-	}
18
-}
19
-
20
-// WithBytes write bytes to a file at Path
21
-func WithBytes(raw []byte) PathOp {
22
-	return func(path Path) error {
23
-		return ioutil.WriteFile(path.Path(), raw, 0644)
24
-	}
25
-}
26
-
27
-// AsUser changes ownership of the file system object at Path
28
-func AsUser(uid, gid int) PathOp {
29
-	return func(path Path) error {
30
-		return os.Chown(path.Path(), uid, gid)
31
-	}
32
-}
33
-
34
-// WithFile creates a file in the directory at path with content
35
-func WithFile(filename, content string, ops ...PathOp) PathOp {
36
-	return func(path Path) error {
37
-		fullpath := filepath.Join(path.Path(), filepath.FromSlash(filename))
38
-		if err := createFile(fullpath, content); err != nil {
39
-			return err
40
-		}
41
-		return applyPathOps(&File{path: fullpath}, ops)
42
-	}
43
-}
44
-
45
-func createFile(fullpath string, content string) error {
46
-	return ioutil.WriteFile(fullpath, []byte(content), 0644)
47
-}
48
-
49
-// WithFiles creates all the files in the directory at path with their content
50
-func WithFiles(files map[string]string) PathOp {
51
-	return func(path Path) error {
52
-		for filename, content := range files {
53
-			fullpath := filepath.Join(path.Path(), filepath.FromSlash(filename))
54
-			if err := createFile(fullpath, content); err != nil {
55
-				return err
56
-			}
57
-		}
58
-		return nil
59
-	}
60
-}
61
-
62
-// FromDir copies the directory tree from the source path into the new Dir
63
-func FromDir(source string) PathOp {
64
-	return func(path Path) error {
65
-		return copyDirectory(source, path.Path())
66
-	}
67
-}
68
-
69
-// WithDir creates a subdirectory in the directory at path. Additional PathOp
70
-// can be used to modify the subdirectory
71
-func WithDir(name string, ops ...PathOp) PathOp {
72
-	return func(path Path) error {
73
-		fullpath := filepath.Join(path.Path(), filepath.FromSlash(name))
74
-		err := os.MkdirAll(fullpath, 0755)
75
-		if err != nil {
76
-			return err
77
-		}
78
-		return applyPathOps(&Dir{path: fullpath}, ops)
79
-	}
80
-}
81
-
82
-func applyPathOps(path Path, ops []PathOp) error {
83
-	for _, op := range ops {
84
-		if err := op(path); err != nil {
85
-			return err
86
-		}
87
-	}
88
-	return nil
89
-}
90
-
91
-// WithMode sets the file mode on the directory or file at path
92
-func WithMode(mode os.FileMode) PathOp {
93
-	return func(path Path) error {
94
-		return os.Chmod(path.Path(), mode)
95
-	}
96
-}
97
-
98
-func copyDirectory(source, dest string) error {
99
-	entries, err := ioutil.ReadDir(source)
100
-	if err != nil {
101
-		return err
102
-	}
103
-	for _, entry := range entries {
104
-		sourcePath := filepath.Join(source, entry.Name())
105
-		destPath := filepath.Join(dest, entry.Name())
106
-		if entry.IsDir() {
107
-			if err := os.Mkdir(destPath, 0755); err != nil {
108
-				return err
109
-			}
110
-			if err := copyDirectory(sourcePath, destPath); err != nil {
111
-				return err
112
-			}
113
-			continue
114
-		}
115
-		if err := copyFile(sourcePath, destPath); err != nil {
116
-			return err
117
-		}
118
-	}
119
-	return nil
120
-}
121
-
122
-func copyFile(source, dest string) error {
123
-	content, err := ioutil.ReadFile(source)
124
-	if err != nil {
125
-		return err
126
-	}
127
-	return ioutil.WriteFile(dest, content, 0644)
128
-}
129
-
130
-// WithSymlink creates a symlink in the directory which links to target.
131
-// Target must be a path relative to the directory.
132
-//
133
-// Note: the argument order is the inverse of os.Symlink to be consistent with
134
-// the other functions in this package.
135
-func WithSymlink(path, target string) PathOp {
136
-	return func(root Path) error {
137
-		return os.Symlink(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path))
138
-	}
139
-}
140
-
141
-// WithHardlink creates a link in the directory which links to target.
142
-// Target must be a path relative to the directory.
143
-//
144
-// Note: the argument order is the inverse of os.Link to be consistent with
145
-// the other functions in this package.
146
-func WithHardlink(path, target string) PathOp {
147
-	return func(root Path) error {
148
-		return os.Link(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path))
149
-	}
150
-}
151
-
152
-// WithTimestamps sets the access and modification times of the file system object
153
-// at path.
154
-func WithTimestamps(atime, mtime time.Time) PathOp {
155
-	return func(root Path) error {
156
-		return os.Chtimes(root.Path(), atime, mtime)
157
-	}
158
-}
159 1
deleted file mode 100644
... ...
@@ -1,283 +0,0 @@
1
-/*Package icmd executes binaries and provides convenient assertions for testing the results.
2
- */
3
-package icmd
4
-
5
-import (
6
-	"bytes"
7
-	"fmt"
8
-	"io"
9
-	"os/exec"
10
-	"strings"
11
-	"sync"
12
-	"time"
13
-
14
-	"github.com/gotestyourself/gotestyourself/assert"
15
-	"github.com/gotestyourself/gotestyourself/assert/cmp"
16
-)
17
-
18
-type helperT interface {
19
-	Helper()
20
-}
21
-
22
-// None is a token to inform Result.Assert that the output should be empty
23
-const None = "[NOTHING]"
24
-
25
-type lockedBuffer struct {
26
-	m   sync.RWMutex
27
-	buf bytes.Buffer
28
-}
29
-
30
-func (buf *lockedBuffer) Write(b []byte) (int, error) {
31
-	buf.m.Lock()
32
-	defer buf.m.Unlock()
33
-	return buf.buf.Write(b)
34
-}
35
-
36
-func (buf *lockedBuffer) String() string {
37
-	buf.m.RLock()
38
-	defer buf.m.RUnlock()
39
-	return buf.buf.String()
40
-}
41
-
42
-// Result stores the result of running a command
43
-type Result struct {
44
-	Cmd      *exec.Cmd
45
-	ExitCode int
46
-	Error    error
47
-	// Timeout is true if the command was killed because it ran for too long
48
-	Timeout   bool
49
-	outBuffer *lockedBuffer
50
-	errBuffer *lockedBuffer
51
-}
52
-
53
-// Assert compares the Result against the Expected struct, and fails the test if
54
-// any of the expectations are not met.
55
-//
56
-// This function is equivalent to assert.Assert(t, result.Equal(exp)).
57
-func (r *Result) Assert(t assert.TestingT, exp Expected) *Result {
58
-	if ht, ok := t.(helperT); ok {
59
-		ht.Helper()
60
-	}
61
-	assert.Assert(t, r.Equal(exp))
62
-	return r
63
-}
64
-
65
-// Equal compares the result to Expected. If the result doesn't match expected
66
-// returns a formatted failure message with the command, stdout, stderr, exit code,
67
-// and any failed expectations.
68
-func (r *Result) Equal(exp Expected) cmp.Comparison {
69
-	return func() cmp.Result {
70
-		return cmp.ResultFromError(r.match(exp))
71
-	}
72
-}
73
-
74
-// Compare the result to Expected and return an error if they do not match.
75
-func (r *Result) Compare(exp Expected) error {
76
-	return r.match(exp)
77
-}
78
-
79
-// nolint: gocyclo
80
-func (r *Result) match(exp Expected) error {
81
-	errors := []string{}
82
-	add := func(format string, args ...interface{}) {
83
-		errors = append(errors, fmt.Sprintf(format, args...))
84
-	}
85
-
86
-	if exp.ExitCode != r.ExitCode {
87
-		add("ExitCode was %d expected %d", r.ExitCode, exp.ExitCode)
88
-	}
89
-	if exp.Timeout != r.Timeout {
90
-		if exp.Timeout {
91
-			add("Expected command to timeout")
92
-		} else {
93
-			add("Expected command to finish, but it hit the timeout")
94
-		}
95
-	}
96
-	if !matchOutput(exp.Out, r.Stdout()) {
97
-		add("Expected stdout to contain %q", exp.Out)
98
-	}
99
-	if !matchOutput(exp.Err, r.Stderr()) {
100
-		add("Expected stderr to contain %q", exp.Err)
101
-	}
102
-	switch {
103
-	// If a non-zero exit code is expected there is going to be an error.
104
-	// Don't require an error message as well as an exit code because the
105
-	// error message is going to be "exit status <code> which is not useful
106
-	case exp.Error == "" && exp.ExitCode != 0:
107
-	case exp.Error == "" && r.Error != nil:
108
-		add("Expected no error")
109
-	case exp.Error != "" && r.Error == nil:
110
-		add("Expected error to contain %q, but there was no error", exp.Error)
111
-	case exp.Error != "" && !strings.Contains(r.Error.Error(), exp.Error):
112
-		add("Expected error to contain %q", exp.Error)
113
-	}
114
-
115
-	if len(errors) == 0 {
116
-		return nil
117
-	}
118
-	return fmt.Errorf("%s\nFailures:\n%s", r, strings.Join(errors, "\n"))
119
-}
120
-
121
-func matchOutput(expected string, actual string) bool {
122
-	switch expected {
123
-	case None:
124
-		return actual == ""
125
-	default:
126
-		return strings.Contains(actual, expected)
127
-	}
128
-}
129
-
130
-func (r *Result) String() string {
131
-	var timeout string
132
-	if r.Timeout {
133
-		timeout = " (timeout)"
134
-	}
135
-
136
-	return fmt.Sprintf(`
137
-Command:  %s
138
-ExitCode: %d%s
139
-Error:    %v
140
-Stdout:   %v
141
-Stderr:   %v
142
-`,
143
-		strings.Join(r.Cmd.Args, " "),
144
-		r.ExitCode,
145
-		timeout,
146
-		r.Error,
147
-		r.Stdout(),
148
-		r.Stderr())
149
-}
150
-
151
-// Expected is the expected output from a Command. This struct is compared to a
152
-// Result struct by Result.Assert().
153
-type Expected struct {
154
-	ExitCode int
155
-	Timeout  bool
156
-	Error    string
157
-	Out      string
158
-	Err      string
159
-}
160
-
161
-// Success is the default expected result. A Success result is one with a 0
162
-// ExitCode.
163
-var Success = Expected{}
164
-
165
-// Stdout returns the stdout of the process as a string
166
-func (r *Result) Stdout() string {
167
-	return r.outBuffer.String()
168
-}
169
-
170
-// Stderr returns the stderr of the process as a string
171
-func (r *Result) Stderr() string {
172
-	return r.errBuffer.String()
173
-}
174
-
175
-// Combined returns the stdout and stderr combined into a single string
176
-func (r *Result) Combined() string {
177
-	return r.outBuffer.String() + r.errBuffer.String()
178
-}
179
-
180
-func (r *Result) setExitError(err error) {
181
-	if err == nil {
182
-		return
183
-	}
184
-	r.Error = err
185
-	r.ExitCode = processExitCode(err)
186
-}
187
-
188
-// Cmd contains the arguments and options for a process to run as part of a test
189
-// suite.
190
-type Cmd struct {
191
-	Command []string
192
-	Timeout time.Duration
193
-	Stdin   io.Reader
194
-	Stdout  io.Writer
195
-	Dir     string
196
-	Env     []string
197
-}
198
-
199
-// Command create a simple Cmd with the specified command and arguments
200
-func Command(command string, args ...string) Cmd {
201
-	return Cmd{Command: append([]string{command}, args...)}
202
-}
203
-
204
-// RunCmd runs a command and returns a Result
205
-func RunCmd(cmd Cmd, cmdOperators ...CmdOp) *Result {
206
-	for _, op := range cmdOperators {
207
-		op(&cmd)
208
-	}
209
-	result := StartCmd(cmd)
210
-	if result.Error != nil {
211
-		return result
212
-	}
213
-	return WaitOnCmd(cmd.Timeout, result)
214
-}
215
-
216
-// RunCommand runs a command with default options, and returns a result
217
-func RunCommand(command string, args ...string) *Result {
218
-	return RunCmd(Command(command, args...))
219
-}
220
-
221
-// StartCmd starts a command, but doesn't wait for it to finish
222
-func StartCmd(cmd Cmd) *Result {
223
-	result := buildCmd(cmd)
224
-	if result.Error != nil {
225
-		return result
226
-	}
227
-	result.setExitError(result.Cmd.Start())
228
-	return result
229
-}
230
-
231
-func buildCmd(cmd Cmd) *Result {
232
-	var execCmd *exec.Cmd
233
-	switch len(cmd.Command) {
234
-	case 1:
235
-		execCmd = exec.Command(cmd.Command[0])
236
-	default:
237
-		execCmd = exec.Command(cmd.Command[0], cmd.Command[1:]...)
238
-	}
239
-	outBuffer := new(lockedBuffer)
240
-	errBuffer := new(lockedBuffer)
241
-
242
-	execCmd.Stdin = cmd.Stdin
243
-	execCmd.Dir = cmd.Dir
244
-	execCmd.Env = cmd.Env
245
-	if cmd.Stdout != nil {
246
-		execCmd.Stdout = io.MultiWriter(outBuffer, cmd.Stdout)
247
-	} else {
248
-		execCmd.Stdout = outBuffer
249
-	}
250
-	execCmd.Stderr = errBuffer
251
-	return &Result{
252
-		Cmd:       execCmd,
253
-		outBuffer: outBuffer,
254
-		errBuffer: errBuffer,
255
-	}
256
-}
257
-
258
-// WaitOnCmd waits for a command to complete. If timeout is non-nil then
259
-// only wait until the timeout.
260
-func WaitOnCmd(timeout time.Duration, result *Result) *Result {
261
-	if timeout == time.Duration(0) {
262
-		result.setExitError(result.Cmd.Wait())
263
-		return result
264
-	}
265
-
266
-	done := make(chan error, 1)
267
-	// Wait for command to exit in a goroutine
268
-	go func() {
269
-		done <- result.Cmd.Wait()
270
-	}()
271
-
272
-	select {
273
-	case <-time.After(timeout):
274
-		killErr := result.Cmd.Process.Kill()
275
-		if killErr != nil {
276
-			fmt.Printf("failed to kill (pid=%d): %v\n", result.Cmd.Process.Pid, killErr)
277
-		}
278
-		result.Timeout = true
279
-	case err := <-done:
280
-		result.setExitError(err)
281
-	}
282
-	return result
283
-}
284 1
deleted file mode 100644
... ...
@@ -1,32 +0,0 @@
1
-package icmd
2
-
3
-import (
4
-	"os/exec"
5
-	"syscall"
6
-
7
-	"github.com/pkg/errors"
8
-)
9
-
10
-// getExitCode returns the ExitStatus of a process from the error returned by
11
-// exec.Run(). If the exit status could not be parsed an error is returned.
12
-func getExitCode(err error) (int, error) {
13
-	if exiterr, ok := err.(*exec.ExitError); ok {
14
-		if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok {
15
-			return procExit.ExitStatus(), nil
16
-		}
17
-	}
18
-	return 0, errors.Wrap(err, "failed to get exit code")
19
-}
20
-
21
-func processExitCode(err error) (exitCode int) {
22
-	if err == nil {
23
-		return 0
24
-	}
25
-	exitCode, exiterr := getExitCode(err)
26
-	if exiterr != nil {
27
-		// TODO: Fix this so we check the error's text.
28
-		// we've failed to retrieve exit code, so we set it to 127
29
-		return 127
30
-	}
31
-	return exitCode
32
-}
33 1
deleted file mode 100644
... ...
@@ -1,4 +0,0 @@
1
-package icmd
2
-
3
-// CmdOp is an operation which modified a Cmd structure used to execute commands
4
-type CmdOp func(*Cmd)
5 1
deleted file mode 100644
... ...
@@ -1,27 +0,0 @@
1
-package format
2
-
3
-import "fmt"
4
-
5
-// Message accepts a msgAndArgs varargs and formats it using fmt.Sprintf
6
-func Message(msgAndArgs ...interface{}) string {
7
-	switch len(msgAndArgs) {
8
-	case 0:
9
-		return ""
10
-	case 1:
11
-		return fmt.Sprintf("%v", msgAndArgs[0])
12
-	default:
13
-		return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
14
-	}
15
-}
16
-
17
-// WithCustomMessage accepts one or two messages and formats them appropriately
18
-func WithCustomMessage(source string, msgAndArgs ...interface{}) string {
19
-	custom := Message(msgAndArgs...)
20
-	switch {
21
-	case custom == "":
22
-		return source
23
-	case source == "":
24
-		return custom
25
-	}
26
-	return fmt.Sprintf("%s: %s", source, custom)
27
-}
28 1
deleted file mode 100644
... ...
@@ -1,163 +0,0 @@
1
-package source
2
-
3
-import (
4
-	"bytes"
5
-	"fmt"
6
-	"go/ast"
7
-	"go/format"
8
-	"go/parser"
9
-	"go/token"
10
-	"os"
11
-	"runtime"
12
-	"strconv"
13
-	"strings"
14
-
15
-	"github.com/pkg/errors"
16
-)
17
-
18
-const baseStackIndex = 1
19
-
20
-// FormattedCallExprArg returns the argument from an ast.CallExpr at the
21
-// index in the call stack. The argument is formatted using FormatNode.
22
-func FormattedCallExprArg(stackIndex int, argPos int) (string, error) {
23
-	args, err := CallExprArgs(stackIndex + 1)
24
-	if err != nil {
25
-		return "", err
26
-	}
27
-	return FormatNode(args[argPos])
28
-}
29
-
30
-func getNodeAtLine(filename string, lineNum int) (ast.Node, error) {
31
-	fileset := token.NewFileSet()
32
-	astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors)
33
-	if err != nil {
34
-		return nil, errors.Wrapf(err, "failed to parse source file: %s", filename)
35
-	}
36
-
37
-	node := scanToLine(fileset, astFile, lineNum)
38
-	if node == nil {
39
-		return nil, errors.Errorf(
40
-			"failed to find an expression on line %d in %s", lineNum, filename)
41
-	}
42
-	return node, nil
43
-}
44
-
45
-func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node {
46
-	v := &scanToLineVisitor{lineNum: lineNum, fileset: fileset}
47
-	ast.Walk(v, node)
48
-	return v.matchedNode
49
-}
50
-
51
-type scanToLineVisitor struct {
52
-	lineNum     int
53
-	matchedNode ast.Node
54
-	fileset     *token.FileSet
55
-}
56
-
57
-func (v *scanToLineVisitor) Visit(node ast.Node) ast.Visitor {
58
-	if node == nil || v.matchedNode != nil {
59
-		return nil
60
-	}
61
-	if v.nodePosition(node).Line == v.lineNum {
62
-		v.matchedNode = node
63
-		return nil
64
-	}
65
-	return v
66
-}
67
-
68
-// In golang 1.9 the line number changed from being the line where the statement
69
-// ended to the line where the statement began.
70
-func (v *scanToLineVisitor) nodePosition(node ast.Node) token.Position {
71
-	if goVersionBefore19 {
72
-		return v.fileset.Position(node.End())
73
-	}
74
-	return v.fileset.Position(node.Pos())
75
-}
76
-
77
-var goVersionBefore19 = isGOVersionBefore19()
78
-
79
-func isGOVersionBefore19() bool {
80
-	version := runtime.Version()
81
-	// not a release version
82
-	if !strings.HasPrefix(version, "go") {
83
-		return false
84
-	}
85
-	version = strings.TrimPrefix(version, "go")
86
-	parts := strings.Split(version, ".")
87
-	if len(parts) < 2 {
88
-		return false
89
-	}
90
-	minor, err := strconv.ParseInt(parts[1], 10, 32)
91
-	return err == nil && parts[0] == "1" && minor < 9
92
-}
93
-
94
-func getCallExprArgs(node ast.Node) ([]ast.Expr, error) {
95
-	visitor := &callExprVisitor{}
96
-	ast.Walk(visitor, node)
97
-	if visitor.expr == nil {
98
-		return nil, errors.New("failed to find call expression")
99
-	}
100
-	return visitor.expr.Args, nil
101
-}
102
-
103
-type callExprVisitor struct {
104
-	expr *ast.CallExpr
105
-}
106
-
107
-func (v *callExprVisitor) Visit(node ast.Node) ast.Visitor {
108
-	if v.expr != nil || node == nil {
109
-		return nil
110
-	}
111
-	debug("visit (%T): %s", node, debugFormatNode{node})
112
-
113
-	if callExpr, ok := node.(*ast.CallExpr); ok {
114
-		v.expr = callExpr
115
-		return nil
116
-	}
117
-	return v
118
-}
119
-
120
-// FormatNode using go/format.Node and return the result as a string
121
-func FormatNode(node ast.Node) (string, error) {
122
-	buf := new(bytes.Buffer)
123
-	err := format.Node(buf, token.NewFileSet(), node)
124
-	return buf.String(), err
125
-}
126
-
127
-// CallExprArgs returns the ast.Expr slice for the args of an ast.CallExpr at
128
-// the index in the call stack.
129
-func CallExprArgs(stackIndex int) ([]ast.Expr, error) {
130
-	_, filename, lineNum, ok := runtime.Caller(baseStackIndex + stackIndex)
131
-	if !ok {
132
-		return nil, errors.New("failed to get call stack")
133
-	}
134
-	debug("call stack position: %s:%d", filename, lineNum)
135
-
136
-	node, err := getNodeAtLine(filename, lineNum)
137
-	if err != nil {
138
-		return nil, err
139
-	}
140
-	debug("found node (%T): %s", node, debugFormatNode{node})
141
-
142
-	return getCallExprArgs(node)
143
-}
144
-
145
-var debugEnabled = os.Getenv("GOTESTYOURSELF_DEBUG") != ""
146
-
147
-func debug(format string, args ...interface{}) {
148
-	if debugEnabled {
149
-		fmt.Fprintf(os.Stderr, "DEBUG: "+format+"\n", args...)
150
-	}
151
-}
152
-
153
-type debugFormatNode struct {
154
-	ast.Node
155
-}
156
-
157
-func (n debugFormatNode) String() string {
158
-	out, err := FormatNode(n.Node)
159
-	if err != nil {
160
-		return fmt.Sprintf("failed to format %s: %s", n.Node, err)
161
-	}
162
-	return out
163
-}
164 1
deleted file mode 100644
... ...
@@ -1,140 +0,0 @@
1
-/*Package poll provides tools for testing asynchronous code.
2
- */
3
-package poll
4
-
5
-import (
6
-	"fmt"
7
-	"time"
8
-)
9
-
10
-// TestingT is the subset of testing.T used by WaitOn
11
-type TestingT interface {
12
-	LogT
13
-	Fatalf(format string, args ...interface{})
14
-}
15
-
16
-// LogT is a logging interface that is passed to the WaitOn check function
17
-type LogT interface {
18
-	Log(args ...interface{})
19
-	Logf(format string, args ...interface{})
20
-}
21
-
22
-type helperT interface {
23
-	Helper()
24
-}
25
-
26
-// Settings are used to configure the behaviour of WaitOn
27
-type Settings struct {
28
-	// Timeout is the maximum time to wait for the condition. Defaults to 10s
29
-	Timeout time.Duration
30
-	// Delay is the time to sleep between checking the condition. Detaults to
31
-	// 1ms
32
-	Delay time.Duration
33
-}
34
-
35
-func defaultConfig() *Settings {
36
-	return &Settings{Timeout: 10 * time.Second, Delay: time.Millisecond}
37
-}
38
-
39
-// SettingOp is a function which accepts and modifies Settings
40
-type SettingOp func(config *Settings)
41
-
42
-// WithDelay sets the delay to wait between polls
43
-func WithDelay(delay time.Duration) SettingOp {
44
-	return func(config *Settings) {
45
-		config.Delay = delay
46
-	}
47
-}
48
-
49
-// WithTimeout sets the timeout
50
-func WithTimeout(timeout time.Duration) SettingOp {
51
-	return func(config *Settings) {
52
-		config.Timeout = timeout
53
-	}
54
-}
55
-
56
-// Result of a check performed by WaitOn
57
-type Result interface {
58
-	// Error indicates that the check failed and polling should stop, and the
59
-	// the has failed
60
-	Error() error
61
-	// Done indicates that polling should stop, and the test should proceed
62
-	Done() bool
63
-	// Message provides the most recent state when polling has not completed
64
-	Message() string
65
-}
66
-
67
-type result struct {
68
-	done    bool
69
-	message string
70
-	err     error
71
-}
72
-
73
-func (r result) Done() bool {
74
-	return r.done
75
-}
76
-
77
-func (r result) Message() string {
78
-	return r.message
79
-}
80
-
81
-func (r result) Error() error {
82
-	return r.err
83
-}
84
-
85
-// Continue returns a Result that indicates to WaitOn that it should continue
86
-// polling. The message text will be used as the failure message if the timeout
87
-// is reached.
88
-func Continue(message string, args ...interface{}) Result {
89
-	return result{message: fmt.Sprintf(message, args...)}
90
-}
91
-
92
-// Success returns a Result where Done() returns true, which indicates to WaitOn
93
-// that it should stop polling and exit without an error.
94
-func Success() Result {
95
-	return result{done: true}
96
-}
97
-
98
-// Error returns a Result that indicates to WaitOn that it should fail the test
99
-// and stop polling.
100
-func Error(err error) Result {
101
-	return result{err: err}
102
-}
103
-
104
-// WaitOn a condition or until a timeout. Poll by calling check and exit when
105
-// check returns a done Result. To fail a test and exit polling with an error
106
-// return a error result.
107
-func WaitOn(t TestingT, check func(t LogT) Result, pollOps ...SettingOp) {
108
-	if ht, ok := t.(helperT); ok {
109
-		ht.Helper()
110
-	}
111
-	config := defaultConfig()
112
-	for _, pollOp := range pollOps {
113
-		pollOp(config)
114
-	}
115
-
116
-	var lastMessage string
117
-	after := time.After(config.Timeout)
118
-	chResult := make(chan Result)
119
-	for {
120
-		go func() {
121
-			chResult <- check(t)
122
-		}()
123
-		select {
124
-		case <-after:
125
-			if lastMessage == "" {
126
-				lastMessage = "first check never completed"
127
-			}
128
-			t.Fatalf("timeout hit after %s: %s", config.Timeout, lastMessage)
129
-		case result := <-chResult:
130
-			switch {
131
-			case result.Error() != nil:
132
-				t.Fatalf("polling check failed: %s", result.Error())
133
-			case result.Done():
134
-				return
135
-			}
136
-			time.Sleep(config.Delay)
137
-			lastMessage = result.Message()
138
-		}
139
-	}
140
-}
141 1
deleted file mode 100644
... ...
@@ -1,81 +0,0 @@
1
-/*Package skip provides functions for skipping based on a condition.
2
- */
3
-package skip
4
-
5
-import (
6
-	"fmt"
7
-	"path"
8
-	"reflect"
9
-	"runtime"
10
-	"strings"
11
-
12
-	"github.com/gotestyourself/gotestyourself/internal/format"
13
-	"github.com/gotestyourself/gotestyourself/internal/source"
14
-)
15
-
16
-type skipT interface {
17
-	Skip(args ...interface{})
18
-	Log(args ...interface{})
19
-}
20
-
21
-type helperT interface {
22
-	Helper()
23
-}
24
-
25
-// BoolOrCheckFunc can be a bool or func() bool, other types will panic
26
-type BoolOrCheckFunc interface{}
27
-
28
-// If skips the test if the check function returns true. The skip message will
29
-// contain the name of the check function. Extra message text can be passed as a
30
-// format string with args
31
-func If(t skipT, condition BoolOrCheckFunc, msgAndArgs ...interface{}) {
32
-	if ht, ok := t.(helperT); ok {
33
-		ht.Helper()
34
-	}
35
-	switch check := condition.(type) {
36
-	case bool:
37
-		ifCondition(t, check, msgAndArgs...)
38
-	case func() bool:
39
-		if check() {
40
-			t.Skip(format.WithCustomMessage(getFunctionName(check), msgAndArgs...))
41
-		}
42
-	default:
43
-		panic(fmt.Sprintf("invalid type for condition arg: %T", check))
44
-	}
45
-}
46
-
47
-func getFunctionName(function func() bool) string {
48
-	funcPath := runtime.FuncForPC(reflect.ValueOf(function).Pointer()).Name()
49
-	return strings.SplitN(path.Base(funcPath), ".", 2)[1]
50
-}
51
-
52
-// IfCondition skips the test if the condition is true. The skip message will
53
-// contain the source of the expression passed as the condition. Extra message
54
-// text can be passed as a format string with args.
55
-//
56
-// Deprecated: Use If() which now accepts bool arguments
57
-func IfCondition(t skipT, condition bool, msgAndArgs ...interface{}) {
58
-	if ht, ok := t.(helperT); ok {
59
-		ht.Helper()
60
-	}
61
-	ifCondition(t, condition, msgAndArgs...)
62
-}
63
-
64
-func ifCondition(t skipT, condition bool, msgAndArgs ...interface{}) {
65
-	if ht, ok := t.(helperT); ok {
66
-		ht.Helper()
67
-	}
68
-	if !condition {
69
-		return
70
-	}
71
-	const (
72
-		stackIndex = 2
73
-		argPos     = 1
74
-	)
75
-	source, err := source.FormattedCallExprArg(stackIndex, argPos)
76
-	if err != nil {
77
-		t.Log(err.Error())
78
-		t.Skip(format.Message(msgAndArgs...))
79
-	}
80
-	t.Skip(format.WithCustomMessage(source, msgAndArgs...))
81
-}
82 1
deleted file mode 100644
... ...
@@ -1,27 +0,0 @@
1
-Copyright (c) 2013, Patrick Mezard
2
-All rights reserved.
3
-
4
-Redistribution and use in source and binary forms, with or without
5
-modification, are permitted provided that the following conditions are
6
-met:
7
-
8
-    Redistributions of source code must retain the above copyright
9
-notice, this list of conditions and the following disclaimer.
10
-    Redistributions in binary form must reproduce the above copyright
11
-notice, this list of conditions and the following disclaimer in the
12
-documentation and/or other materials provided with the distribution.
13
-    The names of its contributors may not be used to endorse or promote
14
-products derived from this software without specific prior written
15
-permission.
16
-
17
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18
-IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20
-PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
-HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
23
-TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 1
deleted file mode 100644
... ...
@@ -1,50 +0,0 @@
1
-go-difflib
2
-==========
3
-
4
-[![Build Status](https://travis-ci.org/pmezard/go-difflib.png?branch=master)](https://travis-ci.org/pmezard/go-difflib)
5
-[![GoDoc](https://godoc.org/github.com/pmezard/go-difflib/difflib?status.svg)](https://godoc.org/github.com/pmezard/go-difflib/difflib)
6
-
7
-Go-difflib is a partial port of python 3 difflib package. Its main goal
8
-was to make unified and context diff available in pure Go, mostly for
9
-testing purposes.
10
-
11
-The following class and functions (and related tests) have be ported:
12
-
13
-* `SequenceMatcher`
14
-* `unified_diff()`
15
-* `context_diff()`
16
-
17
-## Installation
18
-
19
-```bash
20
-$ go get github.com/pmezard/go-difflib/difflib
21
-```
22
-
23
-### Quick Start
24
-
25
-Diffs are configured with Unified (or ContextDiff) structures, and can
26
-be output to an io.Writer or returned as a string.
27
-
28
-```Go
29
-diff := UnifiedDiff{
30
-    A:        difflib.SplitLines("foo\nbar\n"),
31
-    B:        difflib.SplitLines("foo\nbaz\n"),
32
-    FromFile: "Original",
33
-    ToFile:   "Current",
34
-    Context:  3,
35
-}
36
-text, _ := GetUnifiedDiffString(diff)
37
-fmt.Printf(text)
38
-```
39
-
40
-would output:
41
-
42
-```
43
-+++ Current
44
-@@ -1,3 +1,3 @@
45
- foo
46
--bar
47
-+baz
48
-```
49
-
50 1
deleted file mode 100644
... ...
@@ -1,772 +0,0 @@
1
-// Package difflib is a partial port of Python difflib module.
2
-//
3
-// It provides tools to compare sequences of strings and generate textual diffs.
4
-//
5
-// The following class and functions have been ported:
6
-//
7
-// - SequenceMatcher
8
-//
9
-// - unified_diff
10
-//
11
-// - context_diff
12
-//
13
-// Getting unified diffs was the main goal of the port. Keep in mind this code
14
-// is mostly suitable to output text differences in a human friendly way, there
15
-// are no guarantees generated diffs are consumable by patch(1).
16
-package difflib
17
-
18
-import (
19
-	"bufio"
20
-	"bytes"
21
-	"fmt"
22
-	"io"
23
-	"strings"
24
-)
25
-
26
-func min(a, b int) int {
27
-	if a < b {
28
-		return a
29
-	}
30
-	return b
31
-}
32
-
33
-func max(a, b int) int {
34
-	if a > b {
35
-		return a
36
-	}
37
-	return b
38
-}
39
-
40
-func calculateRatio(matches, length int) float64 {
41
-	if length > 0 {
42
-		return 2.0 * float64(matches) / float64(length)
43
-	}
44
-	return 1.0
45
-}
46
-
47
-type Match struct {
48
-	A    int
49
-	B    int
50
-	Size int
51
-}
52
-
53
-type OpCode struct {
54
-	Tag byte
55
-	I1  int
56
-	I2  int
57
-	J1  int
58
-	J2  int
59
-}
60
-
61
-// SequenceMatcher compares sequence of strings. The basic
62
-// algorithm predates, and is a little fancier than, an algorithm
63
-// published in the late 1980's by Ratcliff and Obershelp under the
64
-// hyperbolic name "gestalt pattern matching".  The basic idea is to find
65
-// the longest contiguous matching subsequence that contains no "junk"
66
-// elements (R-O doesn't address junk).  The same idea is then applied
67
-// recursively to the pieces of the sequences to the left and to the right
68
-// of the matching subsequence.  This does not yield minimal edit
69
-// sequences, but does tend to yield matches that "look right" to people.
70
-//
71
-// SequenceMatcher tries to compute a "human-friendly diff" between two
72
-// sequences.  Unlike e.g. UNIX(tm) diff, the fundamental notion is the
73
-// longest *contiguous* & junk-free matching subsequence.  That's what
74
-// catches peoples' eyes.  The Windows(tm) windiff has another interesting
75
-// notion, pairing up elements that appear uniquely in each sequence.
76
-// That, and the method here, appear to yield more intuitive difference
77
-// reports than does diff.  This method appears to be the least vulnerable
78
-// to synching up on blocks of "junk lines", though (like blank lines in
79
-// ordinary text files, or maybe "<P>" lines in HTML files).  That may be
80
-// because this is the only method of the 3 that has a *concept* of
81
-// "junk" <wink>.
82
-//
83
-// Timing:  Basic R-O is cubic time worst case and quadratic time expected
84
-// case.  SequenceMatcher is quadratic time for the worst case and has
85
-// expected-case behavior dependent in a complicated way on how many
86
-// elements the sequences have in common; best case time is linear.
87
-type SequenceMatcher struct {
88
-	a              []string
89
-	b              []string
90
-	b2j            map[string][]int
91
-	IsJunk         func(string) bool
92
-	autoJunk       bool
93
-	bJunk          map[string]struct{}
94
-	matchingBlocks []Match
95
-	fullBCount     map[string]int
96
-	bPopular       map[string]struct{}
97
-	opCodes        []OpCode
98
-}
99
-
100
-func NewMatcher(a, b []string) *SequenceMatcher {
101
-	m := SequenceMatcher{autoJunk: true}
102
-	m.SetSeqs(a, b)
103
-	return &m
104
-}
105
-
106
-func NewMatcherWithJunk(a, b []string, autoJunk bool,
107
-	isJunk func(string) bool) *SequenceMatcher {
108
-
109
-	m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}
110
-	m.SetSeqs(a, b)
111
-	return &m
112
-}
113
-
114
-// Set two sequences to be compared.
115
-func (m *SequenceMatcher) SetSeqs(a, b []string) {
116
-	m.SetSeq1(a)
117
-	m.SetSeq2(b)
118
-}
119
-
120
-// Set the first sequence to be compared. The second sequence to be compared is
121
-// not changed.
122
-//
123
-// SequenceMatcher computes and caches detailed information about the second
124
-// sequence, so if you want to compare one sequence S against many sequences,
125
-// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other
126
-// sequences.
127
-//
128
-// See also SetSeqs() and SetSeq2().
129
-func (m *SequenceMatcher) SetSeq1(a []string) {
130
-	if &a == &m.a {
131
-		return
132
-	}
133
-	m.a = a
134
-	m.matchingBlocks = nil
135
-	m.opCodes = nil
136
-}
137
-
138
-// Set the second sequence to be compared. The first sequence to be compared is
139
-// not changed.
140
-func (m *SequenceMatcher) SetSeq2(b []string) {
141
-	if &b == &m.b {
142
-		return
143
-	}
144
-	m.b = b
145
-	m.matchingBlocks = nil
146
-	m.opCodes = nil
147
-	m.fullBCount = nil
148
-	m.chainB()
149
-}
150
-
151
-func (m *SequenceMatcher) chainB() {
152
-	// Populate line -> index mapping
153
-	b2j := map[string][]int{}
154
-	for i, s := range m.b {
155
-		indices := b2j[s]
156
-		indices = append(indices, i)
157
-		b2j[s] = indices
158
-	}
159
-
160
-	// Purge junk elements
161
-	m.bJunk = map[string]struct{}{}
162
-	if m.IsJunk != nil {
163
-		junk := m.bJunk
164
-		for s, _ := range b2j {
165
-			if m.IsJunk(s) {
166
-				junk[s] = struct{}{}
167
-			}
168
-		}
169
-		for s, _ := range junk {
170
-			delete(b2j, s)
171
-		}
172
-	}
173
-
174
-	// Purge remaining popular elements
175
-	popular := map[string]struct{}{}
176
-	n := len(m.b)
177
-	if m.autoJunk && n >= 200 {
178
-		ntest := n/100 + 1
179
-		for s, indices := range b2j {
180
-			if len(indices) > ntest {
181
-				popular[s] = struct{}{}
182
-			}
183
-		}
184
-		for s, _ := range popular {
185
-			delete(b2j, s)
186
-		}
187
-	}
188
-	m.bPopular = popular
189
-	m.b2j = b2j
190
-}
191
-
192
-func (m *SequenceMatcher) isBJunk(s string) bool {
193
-	_, ok := m.bJunk[s]
194
-	return ok
195
-}
196
-
197
-// Find longest matching block in a[alo:ahi] and b[blo:bhi].
198
-//
199
-// If IsJunk is not defined:
200
-//
201
-// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
202
-//     alo <= i <= i+k <= ahi
203
-//     blo <= j <= j+k <= bhi
204
-// and for all (i',j',k') meeting those conditions,
205
-//     k >= k'
206
-//     i <= i'
207
-//     and if i == i', j <= j'
208
-//
209
-// In other words, of all maximal matching blocks, return one that
210
-// starts earliest in a, and of all those maximal matching blocks that
211
-// start earliest in a, return the one that starts earliest in b.
212
-//
213
-// If IsJunk is defined, first the longest matching block is
214
-// determined as above, but with the additional restriction that no
215
-// junk element appears in the block.  Then that block is extended as
216
-// far as possible by matching (only) junk elements on both sides.  So
217
-// the resulting block never matches on junk except as identical junk
218
-// happens to be adjacent to an "interesting" match.
219
-//
220
-// If no blocks match, return (alo, blo, 0).
221
-func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
222
-	// CAUTION:  stripping common prefix or suffix would be incorrect.
223
-	// E.g.,
224
-	//    ab
225
-	//    acab
226
-	// Longest matching block is "ab", but if common prefix is
227
-	// stripped, it's "a" (tied with "b").  UNIX(tm) diff does so
228
-	// strip, so ends up claiming that ab is changed to acab by
229
-	// inserting "ca" in the middle.  That's minimal but unintuitive:
230
-	// "it's obvious" that someone inserted "ac" at the front.
231
-	// Windiff ends up at the same place as diff, but by pairing up
232
-	// the unique 'b's and then matching the first two 'a's.
233
-	besti, bestj, bestsize := alo, blo, 0
234
-
235
-	// find longest junk-free match
236
-	// during an iteration of the loop, j2len[j] = length of longest
237
-	// junk-free match ending with a[i-1] and b[j]
238
-	j2len := map[int]int{}
239
-	for i := alo; i != ahi; i++ {
240
-		// look at all instances of a[i] in b; note that because
241
-		// b2j has no junk keys, the loop is skipped if a[i] is junk
242
-		newj2len := map[int]int{}
243
-		for _, j := range m.b2j[m.a[i]] {
244
-			// a[i] matches b[j]
245
-			if j < blo {
246
-				continue
247
-			}
248
-			if j >= bhi {
249
-				break
250
-			}
251
-			k := j2len[j-1] + 1
252
-			newj2len[j] = k
253
-			if k > bestsize {
254
-				besti, bestj, bestsize = i-k+1, j-k+1, k
255
-			}
256
-		}
257
-		j2len = newj2len
258
-	}
259
-
260
-	// Extend the best by non-junk elements on each end.  In particular,
261
-	// "popular" non-junk elements aren't in b2j, which greatly speeds
262
-	// the inner loop above, but also means "the best" match so far
263
-	// doesn't contain any junk *or* popular non-junk elements.
264
-	for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) &&
265
-		m.a[besti-1] == m.b[bestj-1] {
266
-		besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
267
-	}
268
-	for besti+bestsize < ahi && bestj+bestsize < bhi &&
269
-		!m.isBJunk(m.b[bestj+bestsize]) &&
270
-		m.a[besti+bestsize] == m.b[bestj+bestsize] {
271
-		bestsize += 1
272
-	}
273
-
274
-	// Now that we have a wholly interesting match (albeit possibly
275
-	// empty!), we may as well suck up the matching junk on each
276
-	// side of it too.  Can't think of a good reason not to, and it
277
-	// saves post-processing the (possibly considerable) expense of
278
-	// figuring out what to do with it.  In the case of an empty
279
-	// interesting match, this is clearly the right thing to do,
280
-	// because no other kind of match is possible in the regions.
281
-	for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) &&
282
-		m.a[besti-1] == m.b[bestj-1] {
283
-		besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
284
-	}
285
-	for besti+bestsize < ahi && bestj+bestsize < bhi &&
286
-		m.isBJunk(m.b[bestj+bestsize]) &&
287
-		m.a[besti+bestsize] == m.b[bestj+bestsize] {
288
-		bestsize += 1
289
-	}
290
-
291
-	return Match{A: besti, B: bestj, Size: bestsize}
292
-}
293
-
294
-// Return list of triples describing matching subsequences.
295
-//
296
-// Each triple is of the form (i, j, n), and means that
297
-// a[i:i+n] == b[j:j+n].  The triples are monotonically increasing in
298
-// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are
299
-// adjacent triples in the list, and the second is not the last triple in the
300
-// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe
301
-// adjacent equal blocks.
302
-//
303
-// The last triple is a dummy, (len(a), len(b), 0), and is the only
304
-// triple with n==0.
305
-func (m *SequenceMatcher) GetMatchingBlocks() []Match {
306
-	if m.matchingBlocks != nil {
307
-		return m.matchingBlocks
308
-	}
309
-
310
-	var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match
311
-	matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {
312
-		match := m.findLongestMatch(alo, ahi, blo, bhi)
313
-		i, j, k := match.A, match.B, match.Size
314
-		if match.Size > 0 {
315
-			if alo < i && blo < j {
316
-				matched = matchBlocks(alo, i, blo, j, matched)
317
-			}
318
-			matched = append(matched, match)
319
-			if i+k < ahi && j+k < bhi {
320
-				matched = matchBlocks(i+k, ahi, j+k, bhi, matched)
321
-			}
322
-		}
323
-		return matched
324
-	}
325
-	matched := matchBlocks(0, len(m.a), 0, len(m.b), nil)
326
-
327
-	// It's possible that we have adjacent equal blocks in the
328
-	// matching_blocks list now.
329
-	nonAdjacent := []Match{}
330
-	i1, j1, k1 := 0, 0, 0
331
-	for _, b := range matched {
332
-		// Is this block adjacent to i1, j1, k1?
333
-		i2, j2, k2 := b.A, b.B, b.Size
334
-		if i1+k1 == i2 && j1+k1 == j2 {
335
-			// Yes, so collapse them -- this just increases the length of
336
-			// the first block by the length of the second, and the first
337
-			// block so lengthened remains the block to compare against.
338
-			k1 += k2
339
-		} else {
340
-			// Not adjacent.  Remember the first block (k1==0 means it's
341
-			// the dummy we started with), and make the second block the
342
-			// new block to compare against.
343
-			if k1 > 0 {
344
-				nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
345
-			}
346
-			i1, j1, k1 = i2, j2, k2
347
-		}
348
-	}
349
-	if k1 > 0 {
350
-		nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
351
-	}
352
-
353
-	nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})
354
-	m.matchingBlocks = nonAdjacent
355
-	return m.matchingBlocks
356
-}
357
-
358
-// Return list of 5-tuples describing how to turn a into b.
359
-//
360
-// Each tuple is of the form (tag, i1, i2, j1, j2).  The first tuple
361
-// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
362
-// tuple preceding it, and likewise for j1 == the previous j2.
363
-//
364
-// The tags are characters, with these meanings:
365
-//
366
-// 'r' (replace):  a[i1:i2] should be replaced by b[j1:j2]
367
-//
368
-// 'd' (delete):   a[i1:i2] should be deleted, j1==j2 in this case.
369
-//
370
-// 'i' (insert):   b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.
371
-//
372
-// 'e' (equal):    a[i1:i2] == b[j1:j2]
373
-func (m *SequenceMatcher) GetOpCodes() []OpCode {
374
-	if m.opCodes != nil {
375
-		return m.opCodes
376
-	}
377
-	i, j := 0, 0
378
-	matching := m.GetMatchingBlocks()
379
-	opCodes := make([]OpCode, 0, len(matching))
380
-	for _, m := range matching {
381
-		//  invariant:  we've pumped out correct diffs to change
382
-		//  a[:i] into b[:j], and the next matching block is
383
-		//  a[ai:ai+size] == b[bj:bj+size]. So we need to pump
384
-		//  out a diff to change a[i:ai] into b[j:bj], pump out
385
-		//  the matching block, and move (i,j) beyond the match
386
-		ai, bj, size := m.A, m.B, m.Size
387
-		tag := byte(0)
388
-		if i < ai && j < bj {
389
-			tag = 'r'
390
-		} else if i < ai {
391
-			tag = 'd'
392
-		} else if j < bj {
393
-			tag = 'i'
394
-		}
395
-		if tag > 0 {
396
-			opCodes = append(opCodes, OpCode{tag, i, ai, j, bj})
397
-		}
398
-		i, j = ai+size, bj+size
399
-		// the list of matching blocks is terminated by a
400
-		// sentinel with size 0
401
-		if size > 0 {
402
-			opCodes = append(opCodes, OpCode{'e', ai, i, bj, j})
403
-		}
404
-	}
405
-	m.opCodes = opCodes
406
-	return m.opCodes
407
-}
408
-
409
-// Isolate change clusters by eliminating ranges with no changes.
410
-//
411
-// Return a generator of groups with up to n lines of context.
412
-// Each group is in the same format as returned by GetOpCodes().
413
-func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
414
-	if n < 0 {
415
-		n = 3
416
-	}
417
-	codes := m.GetOpCodes()
418
-	if len(codes) == 0 {
419
-		codes = []OpCode{OpCode{'e', 0, 1, 0, 1}}
420
-	}
421
-	// Fixup leading and trailing groups if they show no changes.
422
-	if codes[0].Tag == 'e' {
423
-		c := codes[0]
424
-		i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
425
-		codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
426
-	}
427
-	if codes[len(codes)-1].Tag == 'e' {
428
-		c := codes[len(codes)-1]
429
-		i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
430
-		codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
431
-	}
432
-	nn := n + n
433
-	groups := [][]OpCode{}
434
-	group := []OpCode{}
435
-	for _, c := range codes {
436
-		i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
437
-		// End the current group and start a new one whenever
438
-		// there is a large range with no changes.
439
-		if c.Tag == 'e' && i2-i1 > nn {
440
-			group = append(group, OpCode{c.Tag, i1, min(i2, i1+n),
441
-				j1, min(j2, j1+n)})
442
-			groups = append(groups, group)
443
-			group = []OpCode{}
444
-			i1, j1 = max(i1, i2-n), max(j1, j2-n)
445
-		}
446
-		group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
447
-	}
448
-	if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
449
-		groups = append(groups, group)
450
-	}
451
-	return groups
452
-}
453
-
454
-// Return a measure of the sequences' similarity (float in [0,1]).
455
-//
456
-// Where T is the total number of elements in both sequences, and
457
-// M is the number of matches, this is 2.0*M / T.
458
-// Note that this is 1 if the sequences are identical, and 0 if
459
-// they have nothing in common.
460
-//
461
-// .Ratio() is expensive to compute if you haven't already computed
462
-// .GetMatchingBlocks() or .GetOpCodes(), in which case you may
463
-// want to try .QuickRatio() or .RealQuickRation() first to get an
464
-// upper bound.
465
-func (m *SequenceMatcher) Ratio() float64 {
466
-	matches := 0
467
-	for _, m := range m.GetMatchingBlocks() {
468
-		matches += m.Size
469
-	}
470
-	return calculateRatio(matches, len(m.a)+len(m.b))
471
-}
472
-
473
-// Return an upper bound on ratio() relatively quickly.
474
-//
475
-// This isn't defined beyond that it is an upper bound on .Ratio(), and
476
-// is faster to compute.
477
-func (m *SequenceMatcher) QuickRatio() float64 {
478
-	// viewing a and b as multisets, set matches to the cardinality
479
-	// of their intersection; this counts the number of matches
480
-	// without regard to order, so is clearly an upper bound
481
-	if m.fullBCount == nil {
482
-		m.fullBCount = map[string]int{}
483
-		for _, s := range m.b {
484
-			m.fullBCount[s] = m.fullBCount[s] + 1
485
-		}
486
-	}
487
-
488
-	// avail[x] is the number of times x appears in 'b' less the
489
-	// number of times we've seen it in 'a' so far ... kinda
490
-	avail := map[string]int{}
491
-	matches := 0
492
-	for _, s := range m.a {
493
-		n, ok := avail[s]
494
-		if !ok {
495
-			n = m.fullBCount[s]
496
-		}
497
-		avail[s] = n - 1
498
-		if n > 0 {
499
-			matches += 1
500
-		}
501
-	}
502
-	return calculateRatio(matches, len(m.a)+len(m.b))
503
-}
504
-
505
-// Return an upper bound on ratio() very quickly.
506
-//
507
-// This isn't defined beyond that it is an upper bound on .Ratio(), and
508
-// is faster to compute than either .Ratio() or .QuickRatio().
509
-func (m *SequenceMatcher) RealQuickRatio() float64 {
510
-	la, lb := len(m.a), len(m.b)
511
-	return calculateRatio(min(la, lb), la+lb)
512
-}
513
-
514
-// Convert range to the "ed" format
515
-func formatRangeUnified(start, stop int) string {
516
-	// Per the diff spec at http://www.unix.org/single_unix_specification/
517
-	beginning := start + 1 // lines start numbering with one
518
-	length := stop - start
519
-	if length == 1 {
520
-		return fmt.Sprintf("%d", beginning)
521
-	}
522
-	if length == 0 {
523
-		beginning -= 1 // empty ranges begin at line just before the range
524
-	}
525
-	return fmt.Sprintf("%d,%d", beginning, length)
526
-}
527
-
528
-// Unified diff parameters
529
-type UnifiedDiff struct {
530
-	A        []string // First sequence lines
531
-	FromFile string   // First file name
532
-	FromDate string   // First file time
533
-	B        []string // Second sequence lines
534
-	ToFile   string   // Second file name
535
-	ToDate   string   // Second file time
536
-	Eol      string   // Headers end of line, defaults to LF
537
-	Context  int      // Number of context lines
538
-}
539
-
540
-// Compare two sequences of lines; generate the delta as a unified diff.
541
-//
542
-// Unified diffs are a compact way of showing line changes and a few
543
-// lines of context.  The number of context lines is set by 'n' which
544
-// defaults to three.
545
-//
546
-// By default, the diff control lines (those with ---, +++, or @@) are
547
-// created with a trailing newline.  This is helpful so that inputs
548
-// created from file.readlines() result in diffs that are suitable for
549
-// file.writelines() since both the inputs and outputs have trailing
550
-// newlines.
551
-//
552
-// For inputs that do not have trailing newlines, set the lineterm
553
-// argument to "" so that the output will be uniformly newline free.
554
-//
555
-// The unidiff format normally has a header for filenames and modification
556
-// times.  Any or all of these may be specified using strings for
557
-// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
558
-// The modification times are normally expressed in the ISO 8601 format.
559
-func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
560
-	buf := bufio.NewWriter(writer)
561
-	defer buf.Flush()
562
-	wf := func(format string, args ...interface{}) error {
563
-		_, err := buf.WriteString(fmt.Sprintf(format, args...))
564
-		return err
565
-	}
566
-	ws := func(s string) error {
567
-		_, err := buf.WriteString(s)
568
-		return err
569
-	}
570
-
571
-	if len(diff.Eol) == 0 {
572
-		diff.Eol = "\n"
573
-	}
574
-
575
-	started := false
576
-	m := NewMatcher(diff.A, diff.B)
577
-	for _, g := range m.GetGroupedOpCodes(diff.Context) {
578
-		if !started {
579
-			started = true
580
-			fromDate := ""
581
-			if len(diff.FromDate) > 0 {
582
-				fromDate = "\t" + diff.FromDate
583
-			}
584
-			toDate := ""
585
-			if len(diff.ToDate) > 0 {
586
-				toDate = "\t" + diff.ToDate
587
-			}
588
-			if diff.FromFile != "" || diff.ToFile != "" {
589
-				err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
590
-				if err != nil {
591
-					return err
592
-				}
593
-				err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol)
594
-				if err != nil {
595
-					return err
596
-				}
597
-			}
598
-		}
599
-		first, last := g[0], g[len(g)-1]
600
-		range1 := formatRangeUnified(first.I1, last.I2)
601
-		range2 := formatRangeUnified(first.J1, last.J2)
602
-		if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil {
603
-			return err
604
-		}
605
-		for _, c := range g {
606
-			i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
607
-			if c.Tag == 'e' {
608
-				for _, line := range diff.A[i1:i2] {
609
-					if err := ws(" " + line); err != nil {
610
-						return err
611
-					}
612
-				}
613
-				continue
614
-			}
615
-			if c.Tag == 'r' || c.Tag == 'd' {
616
-				for _, line := range diff.A[i1:i2] {
617
-					if err := ws("-" + line); err != nil {
618
-						return err
619
-					}
620
-				}
621
-			}
622
-			if c.Tag == 'r' || c.Tag == 'i' {
623
-				for _, line := range diff.B[j1:j2] {
624
-					if err := ws("+" + line); err != nil {
625
-						return err
626
-					}
627
-				}
628
-			}
629
-		}
630
-	}
631
-	return nil
632
-}
633
-
634
-// Like WriteUnifiedDiff but returns the diff a string.
635
-func GetUnifiedDiffString(diff UnifiedDiff) (string, error) {
636
-	w := &bytes.Buffer{}
637
-	err := WriteUnifiedDiff(w, diff)
638
-	return string(w.Bytes()), err
639
-}
640
-
641
-// Convert range to the "ed" format.
642
-func formatRangeContext(start, stop int) string {
643
-	// Per the diff spec at http://www.unix.org/single_unix_specification/
644
-	beginning := start + 1 // lines start numbering with one
645
-	length := stop - start
646
-	if length == 0 {
647
-		beginning -= 1 // empty ranges begin at line just before the range
648
-	}
649
-	if length <= 1 {
650
-		return fmt.Sprintf("%d", beginning)
651
-	}
652
-	return fmt.Sprintf("%d,%d", beginning, beginning+length-1)
653
-}
654
-
655
-type ContextDiff UnifiedDiff
656
-
657
-// Compare two sequences of lines; generate the delta as a context diff.
658
-//
659
-// Context diffs are a compact way of showing line changes and a few
660
-// lines of context. The number of context lines is set by diff.Context
661
-// which defaults to three.
662
-//
663
-// By default, the diff control lines (those with *** or ---) are
664
-// created with a trailing newline.
665
-//
666
-// For inputs that do not have trailing newlines, set the diff.Eol
667
-// argument to "" so that the output will be uniformly newline free.
668
-//
669
-// The context diff format normally has a header for filenames and
670
-// modification times.  Any or all of these may be specified using
671
-// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate.
672
-// The modification times are normally expressed in the ISO 8601 format.
673
-// If not specified, the strings default to blanks.
674
-func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
675
-	buf := bufio.NewWriter(writer)
676
-	defer buf.Flush()
677
-	var diffErr error
678
-	wf := func(format string, args ...interface{}) {
679
-		_, err := buf.WriteString(fmt.Sprintf(format, args...))
680
-		if diffErr == nil && err != nil {
681
-			diffErr = err
682
-		}
683
-	}
684
-	ws := func(s string) {
685
-		_, err := buf.WriteString(s)
686
-		if diffErr == nil && err != nil {
687
-			diffErr = err
688
-		}
689
-	}
690
-
691
-	if len(diff.Eol) == 0 {
692
-		diff.Eol = "\n"
693
-	}
694
-
695
-	prefix := map[byte]string{
696
-		'i': "+ ",
697
-		'd': "- ",
698
-		'r': "! ",
699
-		'e': "  ",
700
-	}
701
-
702
-	started := false
703
-	m := NewMatcher(diff.A, diff.B)
704
-	for _, g := range m.GetGroupedOpCodes(diff.Context) {
705
-		if !started {
706
-			started = true
707
-			fromDate := ""
708
-			if len(diff.FromDate) > 0 {
709
-				fromDate = "\t" + diff.FromDate
710
-			}
711
-			toDate := ""
712
-			if len(diff.ToDate) > 0 {
713
-				toDate = "\t" + diff.ToDate
714
-			}
715
-			if diff.FromFile != "" || diff.ToFile != "" {
716
-				wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
717
-				wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
718
-			}
719
-		}
720
-
721
-		first, last := g[0], g[len(g)-1]
722
-		ws("***************" + diff.Eol)
723
-
724
-		range1 := formatRangeContext(first.I1, last.I2)
725
-		wf("*** %s ****%s", range1, diff.Eol)
726
-		for _, c := range g {
727
-			if c.Tag == 'r' || c.Tag == 'd' {
728
-				for _, cc := range g {
729
-					if cc.Tag == 'i' {
730
-						continue
731
-					}
732
-					for _, line := range diff.A[cc.I1:cc.I2] {
733
-						ws(prefix[cc.Tag] + line)
734
-					}
735
-				}
736
-				break
737
-			}
738
-		}
739
-
740
-		range2 := formatRangeContext(first.J1, last.J2)
741
-		wf("--- %s ----%s", range2, diff.Eol)
742
-		for _, c := range g {
743
-			if c.Tag == 'r' || c.Tag == 'i' {
744
-				for _, cc := range g {
745
-					if cc.Tag == 'd' {
746
-						continue
747
-					}
748
-					for _, line := range diff.B[cc.J1:cc.J2] {
749
-						ws(prefix[cc.Tag] + line)
750
-					}
751
-				}
752
-				break
753
-			}
754
-		}
755
-	}
756
-	return diffErr
757
-}
758
-
759
-// Like WriteContextDiff but returns the diff a string.
760
-func GetContextDiffString(diff ContextDiff) (string, error) {
761
-	w := &bytes.Buffer{}
762
-	err := WriteContextDiff(w, diff)
763
-	return string(w.Bytes()), err
764
-}
765
-
766
-// Split a string on "\n" while preserving them. The output can be used
767
-// as input for UnifiedDiff and ContextDiff structures.
768
-func SplitLines(s string) []string {
769
-	lines := strings.SplitAfter(s, "\n")
770
-	lines[len(lines)-1] += "\n"
771
-	return lines
772
-}
773 1
new file mode 100644
... ...
@@ -0,0 +1,202 @@
0
+
1
+                                 Apache License
2
+                           Version 2.0, January 2004
3
+                        http://www.apache.org/licenses/
4
+
5
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+   1. Definitions.
8
+
9
+      "License" shall mean the terms and conditions for use, reproduction,
10
+      and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+      "Licensor" shall mean the copyright owner or entity authorized by
13
+      the copyright owner that is granting the License.
14
+
15
+      "Legal Entity" shall mean the union of the acting entity and all
16
+      other entities that control, are controlled by, or are under common
17
+      control with that entity. For the purposes of this definition,
18
+      "control" means (i) the power, direct or indirect, to cause the
19
+      direction or management of such entity, whether by contract or
20
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+      outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+      "You" (or "Your") shall mean an individual or Legal Entity
24
+      exercising permissions granted by this License.
25
+
26
+      "Source" form shall mean the preferred form for making modifications,
27
+      including but not limited to software source code, documentation
28
+      source, and configuration files.
29
+
30
+      "Object" form shall mean any form resulting from mechanical
31
+      transformation or translation of a Source form, including but
32
+      not limited to compiled object code, generated documentation,
33
+      and conversions to other media types.
34
+
35
+      "Work" shall mean the work of authorship, whether in Source or
36
+      Object form, made available under the License, as indicated by a
37
+      copyright notice that is included in or attached to the work
38
+      (an example is provided in the Appendix below).
39
+
40
+      "Derivative Works" shall mean any work, whether in Source or Object
41
+      form, that is based on (or derived from) the Work and for which the
42
+      editorial revisions, annotations, elaborations, or other modifications
43
+      represent, as a whole, an original work of authorship. For the purposes
44
+      of this License, Derivative Works shall not include works that remain
45
+      separable from, or merely link (or bind by name) to the interfaces of,
46
+      the Work and Derivative Works thereof.
47
+
48
+      "Contribution" shall mean any work of authorship, including
49
+      the original version of the Work and any modifications or additions
50
+      to that Work or Derivative Works thereof, that is intentionally
51
+      submitted to Licensor for inclusion in the Work by the copyright owner
52
+      or by an individual or Legal Entity authorized to submit on behalf of
53
+      the copyright owner. For the purposes of this definition, "submitted"
54
+      means any form of electronic, verbal, or written communication sent
55
+      to the Licensor or its representatives, including but not limited to
56
+      communication on electronic mailing lists, source code control systems,
57
+      and issue tracking systems that are managed by, or on behalf of, the
58
+      Licensor for the purpose of discussing and improving the Work, but
59
+      excluding communication that is conspicuously marked or otherwise
60
+      designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+      "Contributor" shall mean Licensor and any individual or Legal Entity
63
+      on behalf of whom a Contribution has been received by Licensor and
64
+      subsequently incorporated within the Work.
65
+
66
+   2. Grant of Copyright License. Subject to the terms and conditions of
67
+      this License, each Contributor hereby grants to You a perpetual,
68
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+      copyright license to reproduce, prepare Derivative Works of,
70
+      publicly display, publicly perform, sublicense, and distribute the
71
+      Work and such Derivative Works in Source or Object form.
72
+
73
+   3. Grant of Patent License. Subject to the terms and conditions of
74
+      this License, each Contributor hereby grants to You a perpetual,
75
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+      (except as stated in this section) patent license to make, have made,
77
+      use, offer to sell, sell, import, and otherwise transfer the Work,
78
+      where such license applies only to those patent claims licensable
79
+      by such Contributor that are necessarily infringed by their
80
+      Contribution(s) alone or by combination of their Contribution(s)
81
+      with the Work to which such Contribution(s) was submitted. If You
82
+      institute patent litigation against any entity (including a
83
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+      or a Contribution incorporated within the Work constitutes direct
85
+      or contributory patent infringement, then any patent licenses
86
+      granted to You under this License for that Work shall terminate
87
+      as of the date such litigation is filed.
88
+
89
+   4. Redistribution. You may reproduce and distribute copies of the
90
+      Work or Derivative Works thereof in any medium, with or without
91
+      modifications, and in Source or Object form, provided that You
92
+      meet the following conditions:
93
+
94
+      (a) You must give any other recipients of the Work or
95
+          Derivative Works a copy of this License; and
96
+
97
+      (b) You must cause any modified files to carry prominent notices
98
+          stating that You changed the files; and
99
+
100
+      (c) You must retain, in the Source form of any Derivative Works
101
+          that You distribute, all copyright, patent, trademark, and
102
+          attribution notices from the Source form of the Work,
103
+          excluding those notices that do not pertain to any part of
104
+          the Derivative Works; and
105
+
106
+      (d) If the Work includes a "NOTICE" text file as part of its
107
+          distribution, then any Derivative Works that You distribute must
108
+          include a readable copy of the attribution notices contained
109
+          within such NOTICE file, excluding those notices that do not
110
+          pertain to any part of the Derivative Works, in at least one
111
+          of the following places: within a NOTICE text file distributed
112
+          as part of the Derivative Works; within the Source form or
113
+          documentation, if provided along with the Derivative Works; or,
114
+          within a display generated by the Derivative Works, if and
115
+          wherever such third-party notices normally appear. The contents
116
+          of the NOTICE file are for informational purposes only and
117
+          do not modify the License. You may add Your own attribution
118
+          notices within Derivative Works that You distribute, alongside
119
+          or as an addendum to the NOTICE text from the Work, provided
120
+          that such additional attribution notices cannot be construed
121
+          as modifying the License.
122
+
123
+      You may add Your own copyright statement to Your modifications and
124
+      may provide additional or different license terms and conditions
125
+      for use, reproduction, or distribution of Your modifications, or
126
+      for any such Derivative Works as a whole, provided Your use,
127
+      reproduction, and distribution of the Work otherwise complies with
128
+      the conditions stated in this License.
129
+
130
+   5. Submission of Contributions. Unless You explicitly state otherwise,
131
+      any Contribution intentionally submitted for inclusion in the Work
132
+      by You to the Licensor shall be under the terms and conditions of
133
+      this License, without any additional terms or conditions.
134
+      Notwithstanding the above, nothing herein shall supersede or modify
135
+      the terms of any separate license agreement you may have executed
136
+      with Licensor regarding such Contributions.
137
+
138
+   6. Trademarks. This License does not grant permission to use the trade
139
+      names, trademarks, service marks, or product names of the Licensor,
140
+      except as required for reasonable and customary use in describing the
141
+      origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+   7. Disclaimer of Warranty. Unless required by applicable law or
144
+      agreed to in writing, Licensor provides the Work (and each
145
+      Contributor provides its Contributions) on an "AS IS" BASIS,
146
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+      implied, including, without limitation, any warranties or conditions
148
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+      PARTICULAR PURPOSE. You are solely responsible for determining the
150
+      appropriateness of using or redistributing the Work and assume any
151
+      risks associated with Your exercise of permissions under this License.
152
+
153
+   8. Limitation of Liability. In no event and under no legal theory,
154
+      whether in tort (including negligence), contract, or otherwise,
155
+      unless required by applicable law (such as deliberate and grossly
156
+      negligent acts) or agreed to in writing, shall any Contributor be
157
+      liable to You for damages, including any direct, indirect, special,
158
+      incidental, or consequential damages of any character arising as a
159
+      result of this License or out of the use or inability to use the
160
+      Work (including but not limited to damages for loss of goodwill,
161
+      work stoppage, computer failure or malfunction, or any and all
162
+      other commercial damages or losses), even if such Contributor
163
+      has been advised of the possibility of such damages.
164
+
165
+   9. Accepting Warranty or Additional Liability. While redistributing
166
+      the Work or Derivative Works thereof, You may choose to offer,
167
+      and charge a fee for, acceptance of support, warranty, indemnity,
168
+      or other liability obligations and/or rights consistent with this
169
+      License. However, in accepting such obligations, You may act only
170
+      on Your own behalf and on Your sole responsibility, not on behalf
171
+      of any other Contributor, and only if You agree to indemnify,
172
+      defend, and hold each Contributor harmless for any liability
173
+      incurred by, or claims asserted against, such Contributor by reason
174
+      of your accepting any such warranty or additional liability.
175
+
176
+   END OF TERMS AND CONDITIONS
177
+
178
+   APPENDIX: How to apply the Apache License to your work.
179
+
180
+      To apply the Apache License to your work, attach the following
181
+      boilerplate notice, with the fields enclosed by brackets "[]"
182
+      replaced with your own identifying information. (Don't include
183
+      the brackets!)  The text should be enclosed in the appropriate
184
+      comment syntax for the file format. We also recommend that a
185
+      file or class name and description of purpose be included on the
186
+      same "printed page" as the copyright notice for easier
187
+      identification within third-party archives.
188
+
189
+   Copyright [yyyy] [name of copyright owner]
190
+
191
+   Licensed under the Apache License, Version 2.0 (the "License");
192
+   you may not use this file except in compliance with the License.
193
+   You may obtain a copy of the License at
194
+
195
+       http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+   Unless required by applicable law or agreed to in writing, software
198
+   distributed under the License is distributed on an "AS IS" BASIS,
199
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+   See the License for the specific language governing permissions and
201
+   limitations under the License.
0 202
new file mode 100644
... ...
@@ -0,0 +1,31 @@
0
+# gotest.tools
1
+
2
+A collection of packages to augment `testing` and support common patterns.
3
+
4
+[![GoDoc](https://godoc.org/gotest.tools?status.svg)](https://godoc.org/gotest.tools)
5
+[![CircleCI](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master.svg?style=shield)](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master)
6
+[![Go Reportcard](https://goreportcard.com/badge/gotest.tools)](https://goreportcard.com/report/gotest.tools)
7
+
8
+
9
+## Packages
10
+
11
+* [assert](http://godoc.org/gotest.tools/assert) -
12
+  compare values and fail the test when a comparison fails
13
+* [env](http://godoc.org/gotest.tools/env) -
14
+  test code which uses environment variables
15
+* [fs](http://godoc.org/gotest.tools/fs) -
16
+  create temporary files and compare a filesystem tree to an expected value
17
+* [golden](http://godoc.org/gotest.tools/golden) -
18
+  compare large multi-line strings against values frozen in golden files
19
+* [icmd](http://godoc.org/gotest.tools/icmd) -
20
+  execute binaries and test the output
21
+* [poll](http://godoc.org/gotest.tools/poll) -
22
+  test asynchronous code by polling until a desired state is reached
23
+* [skip](http://godoc.org/gotest.tools/skip) -
24
+  skip a test and print the source code of the condition used to skip the test
25
+
26
+## Related
27
+
28
+* [gotest.tools/gotestsum](https://github.com/gotestyourself/gotestsum) - go test runner with custom output
29
+* [maxbrunsfeld/counterfeiter](https://github.com/maxbrunsfeld/counterfeiter) - generate fakes for interfaces
30
+* [jonboulle/clockwork](https://github.com/jonboulle/clockwork) - a fake clock for testing code that uses `time`
0 31
new file mode 100644
... ...
@@ -0,0 +1,311 @@
0
+/*Package assert provides assertions for comparing expected values to actual
1
+values. When an assertion fails a helpful error message is printed.
2
+
3
+Assert and Check
4
+
5
+Assert() and Check() both accept a Comparison, and fail the test when the
6
+comparison fails. The one difference is that Assert() will end the test execution
7
+immediately (using t.FailNow()) whereas Check() will fail the test (using t.Fail()),
8
+return the value of the comparison, then proceed with the rest of the test case.
9
+
10
+Example usage
11
+
12
+The example below shows assert used with some common types.
13
+
14
+
15
+	import (
16
+	    "testing"
17
+
18
+	    "gotest.tools/assert"
19
+	    is "gotest.tools/assert/cmp"
20
+	)
21
+
22
+	func TestEverything(t *testing.T) {
23
+	    // booleans
24
+	    assert.Assert(t, ok)
25
+	    assert.Assert(t, !missing)
26
+
27
+	    // primitives
28
+	    assert.Equal(t, count, 1)
29
+	    assert.Equal(t, msg, "the message")
30
+	    assert.Assert(t, total != 10) // NotEqual
31
+
32
+	    // errors
33
+	    assert.NilError(t, closer.Close())
34
+	    assert.Error(t, err, "the exact error message")
35
+	    assert.ErrorContains(t, err, "includes this")
36
+	    assert.ErrorType(t, err, os.IsNotExist)
37
+
38
+	    // complex types
39
+	    assert.DeepEqual(t, result, myStruct{Name: "title"})
40
+	    assert.Assert(t, is.Len(items, 3))
41
+	    assert.Assert(t, len(sequence) != 0) // NotEmpty
42
+	    assert.Assert(t, is.Contains(mapping, "key"))
43
+
44
+	    // pointers and interface
45
+	    assert.Assert(t, is.Nil(ref))
46
+	    assert.Assert(t, ref != nil) // NotNil
47
+	}
48
+
49
+Comparisons
50
+
51
+Package https://godoc.org/gotest.tools/assert/cmp provides
52
+many common comparisons. Additional comparisons can be written to compare
53
+values in other ways. See the example Assert (CustomComparison).
54
+
55
+Automated migration from testify
56
+
57
+gty-migrate-from-testify is a binary which can update source code which uses
58
+testify assertions to use the assertions provided by this package.
59
+
60
+See http://bit.do/cmd-gty-migrate-from-testify.
61
+
62
+
63
+*/
64
+package assert // import "gotest.tools/assert"
65
+
66
+import (
67
+	"fmt"
68
+	"go/ast"
69
+	"go/token"
70
+
71
+	gocmp "github.com/google/go-cmp/cmp"
72
+	"gotest.tools/assert/cmp"
73
+	"gotest.tools/internal/format"
74
+	"gotest.tools/internal/source"
75
+)
76
+
77
+// BoolOrComparison can be a bool, or cmp.Comparison. See Assert() for usage.
78
+type BoolOrComparison interface{}
79
+
80
+// TestingT is the subset of testing.T used by the assert package.
81
+type TestingT interface {
82
+	FailNow()
83
+	Fail()
84
+	Log(args ...interface{})
85
+}
86
+
87
+type helperT interface {
88
+	Helper()
89
+}
90
+
91
+const failureMessage = "assertion failed: "
92
+
93
+// nolint: gocyclo
94
+func assert(
95
+	t TestingT,
96
+	failer func(),
97
+	argSelector argSelector,
98
+	comparison BoolOrComparison,
99
+	msgAndArgs ...interface{},
100
+) bool {
101
+	if ht, ok := t.(helperT); ok {
102
+		ht.Helper()
103
+	}
104
+	var success bool
105
+	switch check := comparison.(type) {
106
+	case bool:
107
+		if check {
108
+			return true
109
+		}
110
+		logFailureFromBool(t, msgAndArgs...)
111
+
112
+	// Undocumented legacy comparison without Result type
113
+	case func() (success bool, message string):
114
+		success = runCompareFunc(t, check, msgAndArgs...)
115
+
116
+	case nil:
117
+		return true
118
+
119
+	case error:
120
+		msg := "error is not nil: "
121
+		t.Log(format.WithCustomMessage(failureMessage+msg+check.Error(), msgAndArgs...))
122
+
123
+	case cmp.Comparison:
124
+		success = runComparison(t, argSelector, check, msgAndArgs...)
125
+
126
+	case func() cmp.Result:
127
+		success = runComparison(t, argSelector, check, msgAndArgs...)
128
+
129
+	default:
130
+		t.Log(fmt.Sprintf("invalid Comparison: %v (%T)", check, check))
131
+	}
132
+
133
+	if success {
134
+		return true
135
+	}
136
+	failer()
137
+	return false
138
+}
139
+
140
+func runCompareFunc(
141
+	t TestingT,
142
+	f func() (success bool, message string),
143
+	msgAndArgs ...interface{},
144
+) bool {
145
+	if ht, ok := t.(helperT); ok {
146
+		ht.Helper()
147
+	}
148
+	if success, message := f(); !success {
149
+		t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...))
150
+		return false
151
+	}
152
+	return true
153
+}
154
+
155
+func logFailureFromBool(t TestingT, msgAndArgs ...interface{}) {
156
+	if ht, ok := t.(helperT); ok {
157
+		ht.Helper()
158
+	}
159
+	const stackIndex = 3 // Assert()/Check(), assert(), formatFailureFromBool()
160
+	const comparisonArgPos = 1
161
+	args, err := source.CallExprArgs(stackIndex)
162
+	if err != nil {
163
+		t.Log(err.Error())
164
+		return
165
+	}
166
+
167
+	msg, err := boolFailureMessage(args[comparisonArgPos])
168
+	if err != nil {
169
+		t.Log(err.Error())
170
+		msg = "expression is false"
171
+	}
172
+
173
+	t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...))
174
+}
175
+
176
+func boolFailureMessage(expr ast.Expr) (string, error) {
177
+	if binaryExpr, ok := expr.(*ast.BinaryExpr); ok && binaryExpr.Op == token.NEQ {
178
+		x, err := source.FormatNode(binaryExpr.X)
179
+		if err != nil {
180
+			return "", err
181
+		}
182
+		y, err := source.FormatNode(binaryExpr.Y)
183
+		if err != nil {
184
+			return "", err
185
+		}
186
+		return x + " is " + y, nil
187
+	}
188
+
189
+	if unaryExpr, ok := expr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.NOT {
190
+		x, err := source.FormatNode(unaryExpr.X)
191
+		if err != nil {
192
+			return "", err
193
+		}
194
+		return x + " is true", nil
195
+	}
196
+
197
+	formatted, err := source.FormatNode(expr)
198
+	if err != nil {
199
+		return "", err
200
+	}
201
+	return "expression is false: " + formatted, nil
202
+}
203
+
204
+// Assert performs a comparison. If the comparison fails the test is marked as
205
+// failed, a failure message is logged, and execution is stopped immediately.
206
+//
207
+// The comparison argument may be one of three types: bool, cmp.Comparison or
208
+// error.
209
+// When called with a bool the failure message will contain the literal source
210
+// code of the expression.
211
+// When called with a cmp.Comparison the comparison is responsible for producing
212
+// a helpful failure message.
213
+// When called with an error a nil value is considered success. A non-nil error
214
+// is a failure, and Error() is used as the failure message.
215
+func Assert(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) {
216
+	if ht, ok := t.(helperT); ok {
217
+		ht.Helper()
218
+	}
219
+	assert(t, t.FailNow, argsFromComparisonCall, comparison, msgAndArgs...)
220
+}
221
+
222
+// Check performs a comparison. If the comparison fails the test is marked as
223
+// failed, a failure message is logged, and Check returns false. Otherwise returns
224
+// true.
225
+//
226
+// See Assert for details about the comparison arg and failure messages.
227
+func Check(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) bool {
228
+	if ht, ok := t.(helperT); ok {
229
+		ht.Helper()
230
+	}
231
+	return assert(t, t.Fail, argsFromComparisonCall, comparison, msgAndArgs...)
232
+}
233
+
234
+// NilError fails the test immediately if err is not nil.
235
+// This is equivalent to Assert(t, err)
236
+func NilError(t TestingT, err error, msgAndArgs ...interface{}) {
237
+	if ht, ok := t.(helperT); ok {
238
+		ht.Helper()
239
+	}
240
+	assert(t, t.FailNow, argsAfterT, err, msgAndArgs...)
241
+}
242
+
243
+// Equal uses the == operator to assert two values are equal and fails the test
244
+// if they are not equal.
245
+//
246
+// If the comparison fails Equal will use the variable names for x and y as part
247
+// of the failure message to identify the actual and expected values.
248
+//
249
+// If either x or y are a multi-line string the failure message will include a
250
+// unified diff of the two values. If the values only differ by whitespace
251
+// the unified diff will be augmented by replacing whitespace characters with
252
+// visible characters to identify the whitespace difference.
253
+//
254
+// This is equivalent to Assert(t, cmp.Equal(x, y)).
255
+func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) {
256
+	if ht, ok := t.(helperT); ok {
257
+		ht.Helper()
258
+	}
259
+	assert(t, t.FailNow, argsAfterT, cmp.Equal(x, y), msgAndArgs...)
260
+}
261
+
262
+// DeepEqual uses google/go-cmp (http://bit.do/go-cmp) to assert two values are
263
+// equal and fails the test if they are not equal.
264
+//
265
+// Package https://godoc.org/gotest.tools/assert/opt provides some additional
266
+// commonly used Options.
267
+//
268
+// This is equivalent to Assert(t, cmp.DeepEqual(x, y)).
269
+func DeepEqual(t TestingT, x, y interface{}, opts ...gocmp.Option) {
270
+	if ht, ok := t.(helperT); ok {
271
+		ht.Helper()
272
+	}
273
+	assert(t, t.FailNow, argsAfterT, cmp.DeepEqual(x, y, opts...))
274
+}
275
+
276
+// Error fails the test if err is nil, or the error message is not the expected
277
+// message.
278
+// Equivalent to Assert(t, cmp.Error(err, message)).
279
+func Error(t TestingT, err error, message string, msgAndArgs ...interface{}) {
280
+	if ht, ok := t.(helperT); ok {
281
+		ht.Helper()
282
+	}
283
+	assert(t, t.FailNow, argsAfterT, cmp.Error(err, message), msgAndArgs...)
284
+}
285
+
286
+// ErrorContains fails the test if err is nil, or the error message does not
287
+// contain the expected substring.
288
+// Equivalent to Assert(t, cmp.ErrorContains(err, substring)).
289
+func ErrorContains(t TestingT, err error, substring string, msgAndArgs ...interface{}) {
290
+	if ht, ok := t.(helperT); ok {
291
+		ht.Helper()
292
+	}
293
+	assert(t, t.FailNow, argsAfterT, cmp.ErrorContains(err, substring), msgAndArgs...)
294
+}
295
+
296
+// ErrorType fails the test if err is nil, or err is not the expected type.
297
+//
298
+// Expected can be one of:
299
+// a func(error) bool which returns true if the error is the expected type,
300
+// an instance of (or a pointer to) a struct of the expected type,
301
+// a pointer to an interface the error is expected to implement,
302
+// a reflect.Type of the expected struct or interface.
303
+//
304
+// Equivalent to Assert(t, cmp.ErrorType(err, expected)).
305
+func ErrorType(t TestingT, err error, expected interface{}, msgAndArgs ...interface{}) {
306
+	if ht, ok := t.(helperT); ok {
307
+		ht.Helper()
308
+	}
309
+	assert(t, t.FailNow, argsAfterT, cmp.ErrorType(err, expected), msgAndArgs...)
310
+}
0 311
new file mode 100644
... ...
@@ -0,0 +1,312 @@
0
+/*Package cmp provides Comparisons for Assert and Check*/
1
+package cmp // import "gotest.tools/assert/cmp"
2
+
3
+import (
4
+	"fmt"
5
+	"reflect"
6
+	"strings"
7
+
8
+	"github.com/google/go-cmp/cmp"
9
+	"gotest.tools/internal/format"
10
+)
11
+
12
+// Comparison is a function which compares values and returns ResultSuccess if
13
+// the actual value matches the expected value. If the values do not match the
14
+// Result will contain a message about why it failed.
15
+type Comparison func() Result
16
+
17
+// DeepEqual compares two values using google/go-cmp (http://bit.do/go-cmp)
18
+// and succeeds if the values are equal.
19
+//
20
+// The comparison can be customized using comparison Options.
21
+// Package https://godoc.org/gotest.tools/assert/opt provides some additional
22
+// commonly used Options.
23
+func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison {
24
+	return func() (result Result) {
25
+		defer func() {
26
+			if panicmsg, handled := handleCmpPanic(recover()); handled {
27
+				result = ResultFailure(panicmsg)
28
+			}
29
+		}()
30
+		diff := cmp.Diff(x, y, opts...)
31
+		if diff == "" {
32
+			return ResultSuccess
33
+		}
34
+		return multiLineDiffResult(diff)
35
+	}
36
+}
37
+
38
+func handleCmpPanic(r interface{}) (string, bool) {
39
+	if r == nil {
40
+		return "", false
41
+	}
42
+	panicmsg, ok := r.(string)
43
+	if !ok {
44
+		panic(r)
45
+	}
46
+	switch {
47
+	case strings.HasPrefix(panicmsg, "cannot handle unexported field"):
48
+		return panicmsg, true
49
+	}
50
+	panic(r)
51
+}
52
+
53
+func toResult(success bool, msg string) Result {
54
+	if success {
55
+		return ResultSuccess
56
+	}
57
+	return ResultFailure(msg)
58
+}
59
+
60
+// Equal succeeds if x == y. See assert.Equal for full documentation.
61
+func Equal(x, y interface{}) Comparison {
62
+	return func() Result {
63
+		switch {
64
+		case x == y:
65
+			return ResultSuccess
66
+		case isMultiLineStringCompare(x, y):
67
+			diff := format.UnifiedDiff(format.DiffConfig{A: x.(string), B: y.(string)})
68
+			return multiLineDiffResult(diff)
69
+		}
70
+		return ResultFailureTemplate(`
71
+			{{- .Data.x}} (
72
+				{{- with callArg 0 }}{{ formatNode . }} {{end -}}
73
+				{{- printf "%T" .Data.x -}}
74
+			) != {{ .Data.y}} (
75
+				{{- with callArg 1 }}{{ formatNode . }} {{end -}}
76
+				{{- printf "%T" .Data.y -}}
77
+			)`,
78
+			map[string]interface{}{"x": x, "y": y})
79
+	}
80
+}
81
+
82
+func isMultiLineStringCompare(x, y interface{}) bool {
83
+	strX, ok := x.(string)
84
+	if !ok {
85
+		return false
86
+	}
87
+	strY, ok := y.(string)
88
+	if !ok {
89
+		return false
90
+	}
91
+	return strings.Contains(strX, "\n") || strings.Contains(strY, "\n")
92
+}
93
+
94
+func multiLineDiffResult(diff string) Result {
95
+	return ResultFailureTemplate(`
96
+--- {{ with callArg 0 }}{{ formatNode . }}{{else}}←{{end}}
97
+{{ .Data.diff }}`,
98
+		map[string]interface{}{"diff": diff})
99
+}
100
+
101
+// Len succeeds if the sequence has the expected length.
102
+func Len(seq interface{}, expected int) Comparison {
103
+	return func() (result Result) {
104
+		defer func() {
105
+			if e := recover(); e != nil {
106
+				result = ResultFailure(fmt.Sprintf("type %T does not have a length", seq))
107
+			}
108
+		}()
109
+		value := reflect.ValueOf(seq)
110
+		length := value.Len()
111
+		if length == expected {
112
+			return ResultSuccess
113
+		}
114
+		msg := fmt.Sprintf("expected %s (length %d) to have length %d", seq, length, expected)
115
+		return ResultFailure(msg)
116
+	}
117
+}
118
+
119
+// Contains succeeds if item is in collection. Collection may be a string, map,
120
+// slice, or array.
121
+//
122
+// If collection is a string, item must also be a string, and is compared using
123
+// strings.Contains().
124
+// If collection is a Map, contains will succeed if item is a key in the map.
125
+// If collection is a slice or array, item is compared to each item in the
126
+// sequence using reflect.DeepEqual().
127
+func Contains(collection interface{}, item interface{}) Comparison {
128
+	return func() Result {
129
+		colValue := reflect.ValueOf(collection)
130
+		if !colValue.IsValid() {
131
+			return ResultFailure(fmt.Sprintf("nil does not contain items"))
132
+		}
133
+		msg := fmt.Sprintf("%v does not contain %v", collection, item)
134
+
135
+		itemValue := reflect.ValueOf(item)
136
+		switch colValue.Type().Kind() {
137
+		case reflect.String:
138
+			if itemValue.Type().Kind() != reflect.String {
139
+				return ResultFailure("string may only contain strings")
140
+			}
141
+			return toResult(
142
+				strings.Contains(colValue.String(), itemValue.String()),
143
+				fmt.Sprintf("string %q does not contain %q", collection, item))
144
+
145
+		case reflect.Map:
146
+			if itemValue.Type() != colValue.Type().Key() {
147
+				return ResultFailure(fmt.Sprintf(
148
+					"%v can not contain a %v key", colValue.Type(), itemValue.Type()))
149
+			}
150
+			return toResult(colValue.MapIndex(itemValue).IsValid(), msg)
151
+
152
+		case reflect.Slice, reflect.Array:
153
+			for i := 0; i < colValue.Len(); i++ {
154
+				if reflect.DeepEqual(colValue.Index(i).Interface(), item) {
155
+					return ResultSuccess
156
+				}
157
+			}
158
+			return ResultFailure(msg)
159
+		default:
160
+			return ResultFailure(fmt.Sprintf("type %T does not contain items", collection))
161
+		}
162
+	}
163
+}
164
+
165
+// Panics succeeds if f() panics.
166
+func Panics(f func()) Comparison {
167
+	return func() (result Result) {
168
+		defer func() {
169
+			if err := recover(); err != nil {
170
+				result = ResultSuccess
171
+			}
172
+		}()
173
+		f()
174
+		return ResultFailure("did not panic")
175
+	}
176
+}
177
+
178
+// Error succeeds if err is a non-nil error, and the error message equals the
179
+// expected message.
180
+func Error(err error, message string) Comparison {
181
+	return func() Result {
182
+		switch {
183
+		case err == nil:
184
+			return ResultFailure("expected an error, got nil")
185
+		case err.Error() != message:
186
+			return ResultFailure(fmt.Sprintf(
187
+				"expected error %q, got %+v", message, err))
188
+		}
189
+		return ResultSuccess
190
+	}
191
+}
192
+
193
+// ErrorContains succeeds if err is a non-nil error, and the error message contains
194
+// the expected substring.
195
+func ErrorContains(err error, substring string) Comparison {
196
+	return func() Result {
197
+		switch {
198
+		case err == nil:
199
+			return ResultFailure("expected an error, got nil")
200
+		case !strings.Contains(err.Error(), substring):
201
+			return ResultFailure(fmt.Sprintf(
202
+				"expected error to contain %q, got %+v", substring, err))
203
+		}
204
+		return ResultSuccess
205
+	}
206
+}
207
+
208
+// Nil succeeds if obj is a nil interface, pointer, or function.
209
+//
210
+// Use NilError() for comparing errors. Use Len(obj, 0) for comparing slices,
211
+// maps, and channels.
212
+func Nil(obj interface{}) Comparison {
213
+	msgFunc := func(value reflect.Value) string {
214
+		return fmt.Sprintf("%v (type %s) is not nil", reflect.Indirect(value), value.Type())
215
+	}
216
+	return isNil(obj, msgFunc)
217
+}
218
+
219
+func isNil(obj interface{}, msgFunc func(reflect.Value) string) Comparison {
220
+	return func() Result {
221
+		if obj == nil {
222
+			return ResultSuccess
223
+		}
224
+		value := reflect.ValueOf(obj)
225
+		kind := value.Type().Kind()
226
+		if kind >= reflect.Chan && kind <= reflect.Slice {
227
+			if value.IsNil() {
228
+				return ResultSuccess
229
+			}
230
+			return ResultFailure(msgFunc(value))
231
+		}
232
+
233
+		return ResultFailure(fmt.Sprintf("%v (type %s) can not be nil", value, value.Type()))
234
+	}
235
+}
236
+
237
+// ErrorType succeeds if err is not nil and is of the expected type.
238
+//
239
+// Expected can be one of:
240
+// a func(error) bool which returns true if the error is the expected type,
241
+// an instance of (or a pointer to) a struct of the expected type,
242
+// a pointer to an interface the error is expected to implement,
243
+// a reflect.Type of the expected struct or interface.
244
+func ErrorType(err error, expected interface{}) Comparison {
245
+	return func() Result {
246
+		switch expectedType := expected.(type) {
247
+		case func(error) bool:
248
+			return cmpErrorTypeFunc(err, expectedType)
249
+		case reflect.Type:
250
+			if expectedType.Kind() == reflect.Interface {
251
+				return cmpErrorTypeImplementsType(err, expectedType)
252
+			}
253
+			return cmpErrorTypeEqualType(err, expectedType)
254
+		case nil:
255
+			return ResultFailure(fmt.Sprintf("invalid type for expected: nil"))
256
+		}
257
+
258
+		expectedType := reflect.TypeOf(expected)
259
+		switch {
260
+		case expectedType.Kind() == reflect.Struct, isPtrToStruct(expectedType):
261
+			return cmpErrorTypeEqualType(err, expectedType)
262
+		case isPtrToInterface(expectedType):
263
+			return cmpErrorTypeImplementsType(err, expectedType.Elem())
264
+		}
265
+		return ResultFailure(fmt.Sprintf("invalid type for expected: %T", expected))
266
+	}
267
+}
268
+
269
+func cmpErrorTypeFunc(err error, f func(error) bool) Result {
270
+	if f(err) {
271
+		return ResultSuccess
272
+	}
273
+	actual := "nil"
274
+	if err != nil {
275
+		actual = fmt.Sprintf("%s (%T)", err, err)
276
+	}
277
+	return ResultFailureTemplate(`error is {{ .Data.actual }}
278
+		{{- with callArg 1 }}, not {{ formatNode . }}{{end -}}`,
279
+		map[string]interface{}{"actual": actual})
280
+}
281
+
282
+func cmpErrorTypeEqualType(err error, expectedType reflect.Type) Result {
283
+	if err == nil {
284
+		return ResultFailure(fmt.Sprintf("error is nil, not %s", expectedType))
285
+	}
286
+	errValue := reflect.ValueOf(err)
287
+	if errValue.Type() == expectedType {
288
+		return ResultSuccess
289
+	}
290
+	return ResultFailure(fmt.Sprintf("error is %s (%T), not %s", err, err, expectedType))
291
+}
292
+
293
+func cmpErrorTypeImplementsType(err error, expectedType reflect.Type) Result {
294
+	if err == nil {
295
+		return ResultFailure(fmt.Sprintf("error is nil, not %s", expectedType))
296
+	}
297
+	errValue := reflect.ValueOf(err)
298
+	if errValue.Type().Implements(expectedType) {
299
+		return ResultSuccess
300
+	}
301
+	return ResultFailure(fmt.Sprintf("error is %s (%T), not %s", err, err, expectedType))
302
+}
303
+
304
+func isPtrToInterface(typ reflect.Type) bool {
305
+	return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Interface
306
+}
307
+
308
+func isPtrToStruct(typ reflect.Type) bool {
309
+	return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct
310
+}
0 311
new file mode 100644
... ...
@@ -0,0 +1,94 @@
0
+package cmp
1
+
2
+import (
3
+	"bytes"
4
+	"fmt"
5
+	"go/ast"
6
+	"text/template"
7
+
8
+	"gotest.tools/internal/source"
9
+)
10
+
11
+// Result of a Comparison.
12
+type Result interface {
13
+	Success() bool
14
+}
15
+
16
+type result struct {
17
+	success bool
18
+	message string
19
+}
20
+
21
+func (r result) Success() bool {
22
+	return r.success
23
+}
24
+
25
+func (r result) FailureMessage() string {
26
+	return r.message
27
+}
28
+
29
+// ResultSuccess is a constant which is returned by a ComparisonWithResult to
30
+// indicate success.
31
+var ResultSuccess = result{success: true}
32
+
33
+// ResultFailure returns a failed Result with a failure message.
34
+func ResultFailure(message string) Result {
35
+	return result{message: message}
36
+}
37
+
38
+// ResultFromError returns ResultSuccess if err is nil. Otherwise ResultFailure
39
+// is returned with the error message as the failure message.
40
+func ResultFromError(err error) Result {
41
+	if err == nil {
42
+		return ResultSuccess
43
+	}
44
+	return ResultFailure(err.Error())
45
+}
46
+
47
+type templatedResult struct {
48
+	success  bool
49
+	template string
50
+	data     map[string]interface{}
51
+}
52
+
53
+func (r templatedResult) Success() bool {
54
+	return r.success
55
+}
56
+
57
+func (r templatedResult) FailureMessage(args []ast.Expr) string {
58
+	msg, err := renderMessage(r, args)
59
+	if err != nil {
60
+		return fmt.Sprintf("failed to render failure message: %s", err)
61
+	}
62
+	return msg
63
+}
64
+
65
+// ResultFailureTemplate returns a Result with a template string and data which
66
+// can be used to format a failure message. The template may access data from .Data,
67
+// the comparison args with the callArg function, and the formatNode function may
68
+// be used to format the call args.
69
+func ResultFailureTemplate(template string, data map[string]interface{}) Result {
70
+	return templatedResult{template: template, data: data}
71
+}
72
+
73
+func renderMessage(result templatedResult, args []ast.Expr) (string, error) {
74
+	tmpl := template.New("failure").Funcs(template.FuncMap{
75
+		"formatNode": source.FormatNode,
76
+		"callArg": func(index int) ast.Expr {
77
+			if index >= len(args) {
78
+				return nil
79
+			}
80
+			return args[index]
81
+		},
82
+	})
83
+	var err error
84
+	tmpl, err = tmpl.Parse(result.template)
85
+	if err != nil {
86
+		return "", err
87
+	}
88
+	buf := new(bytes.Buffer)
89
+	err = tmpl.Execute(buf, map[string]interface{}{
90
+		"Data": result.data,
91
+	})
92
+	return buf.String(), err
93
+}
0 94
new file mode 100644
... ...
@@ -0,0 +1,107 @@
0
+package assert
1
+
2
+import (
3
+	"fmt"
4
+	"go/ast"
5
+
6
+	"gotest.tools/assert/cmp"
7
+	"gotest.tools/internal/format"
8
+	"gotest.tools/internal/source"
9
+)
10
+
11
+func runComparison(
12
+	t TestingT,
13
+	argSelector argSelector,
14
+	f cmp.Comparison,
15
+	msgAndArgs ...interface{},
16
+) bool {
17
+	if ht, ok := t.(helperT); ok {
18
+		ht.Helper()
19
+	}
20
+	result := f()
21
+	if result.Success() {
22
+		return true
23
+	}
24
+
25
+	var message string
26
+	switch typed := result.(type) {
27
+	case resultWithComparisonArgs:
28
+		const stackIndex = 3 // Assert/Check, assert, runComparison
29
+		args, err := source.CallExprArgs(stackIndex)
30
+		if err != nil {
31
+			t.Log(err.Error())
32
+		}
33
+		message = typed.FailureMessage(filterPrintableExpr(argSelector(args)))
34
+	case resultBasic:
35
+		message = typed.FailureMessage()
36
+	default:
37
+		message = fmt.Sprintf("comparison returned invalid Result type: %T", result)
38
+	}
39
+
40
+	t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...))
41
+	return false
42
+}
43
+
44
+type resultWithComparisonArgs interface {
45
+	FailureMessage(args []ast.Expr) string
46
+}
47
+
48
+type resultBasic interface {
49
+	FailureMessage() string
50
+}
51
+
52
+// filterPrintableExpr filters the ast.Expr slice to only include Expr that are
53
+// easy to read when printed and contain relevant information to an assertion.
54
+//
55
+// Ident and SelectorExpr are included because they print nicely and the variable
56
+// names may provide additional context to their values.
57
+// BasicLit and CompositeLit are excluded because their source is equivalent to
58
+// their value, which is already available.
59
+// Other types are ignored for now, but could be added if they are relevant.
60
+func filterPrintableExpr(args []ast.Expr) []ast.Expr {
61
+	result := make([]ast.Expr, len(args))
62
+	for i, arg := range args {
63
+		if isShortPrintableExpr(arg) {
64
+			result[i] = arg
65
+			continue
66
+		}
67
+
68
+		if starExpr, ok := arg.(*ast.StarExpr); ok {
69
+			result[i] = starExpr.X
70
+			continue
71
+		}
72
+		result[i] = nil
73
+	}
74
+	return result
75
+}
76
+
77
+func isShortPrintableExpr(expr ast.Expr) bool {
78
+	switch expr.(type) {
79
+	case *ast.Ident, *ast.SelectorExpr, *ast.IndexExpr, *ast.SliceExpr:
80
+		return true
81
+	case *ast.BinaryExpr, *ast.UnaryExpr:
82
+		return true
83
+	default:
84
+		// CallExpr, ParenExpr, TypeAssertExpr, KeyValueExpr, StarExpr
85
+		return false
86
+	}
87
+}
88
+
89
+type argSelector func([]ast.Expr) []ast.Expr
90
+
91
+func argsAfterT(args []ast.Expr) []ast.Expr {
92
+	if len(args) < 1 {
93
+		return nil
94
+	}
95
+	return args[1:]
96
+}
97
+
98
+func argsFromComparisonCall(args []ast.Expr) []ast.Expr {
99
+	if len(args) < 1 {
100
+		return nil
101
+	}
102
+	if callExpr, ok := args[1].(*ast.CallExpr); ok {
103
+		return callExpr.Args
104
+	}
105
+	return nil
106
+}
0 107
new file mode 100644
... ...
@@ -0,0 +1,111 @@
0
+/*Package env provides functions to test code that read environment variables
1
+or the current working directory.
2
+*/
3
+package env // import "gotest.tools/env"
4
+
5
+import (
6
+	"os"
7
+	"strings"
8
+
9
+	"gotest.tools/assert"
10
+	"gotest.tools/x/subtest"
11
+)
12
+
13
+type helperT interface {
14
+	Helper()
15
+}
16
+
17
+// Patch changes the value of an environment variable, and returns a
18
+// function which will reset the the value of that variable back to the
19
+// previous state.
20
+func Patch(t assert.TestingT, key, value string) func() {
21
+	if ht, ok := t.(helperT); ok {
22
+		ht.Helper()
23
+	}
24
+	oldValue, ok := os.LookupEnv(key)
25
+	assert.NilError(t, os.Setenv(key, value))
26
+	cleanup := func() {
27
+		if ht, ok := t.(helperT); ok {
28
+			ht.Helper()
29
+		}
30
+		if !ok {
31
+			assert.NilError(t, os.Unsetenv(key))
32
+			return
33
+		}
34
+		assert.NilError(t, os.Setenv(key, oldValue))
35
+	}
36
+	if tc, ok := t.(subtest.TestContext); ok {
37
+		tc.AddCleanup(cleanup)
38
+	}
39
+	return cleanup
40
+}
41
+
42
+// PatchAll sets the environment to env, and returns a function which will
43
+// reset the environment back to the previous state.
44
+func PatchAll(t assert.TestingT, env map[string]string) func() {
45
+	if ht, ok := t.(helperT); ok {
46
+		ht.Helper()
47
+	}
48
+	oldEnv := os.Environ()
49
+	os.Clearenv()
50
+
51
+	for key, value := range env {
52
+		assert.NilError(t, os.Setenv(key, value), "setenv %s=%s", key, value)
53
+	}
54
+	cleanup := func() {
55
+		if ht, ok := t.(helperT); ok {
56
+			ht.Helper()
57
+		}
58
+		os.Clearenv()
59
+		for key, oldVal := range ToMap(oldEnv) {
60
+			assert.NilError(t, os.Setenv(key, oldVal), "setenv %s=%s", key, oldVal)
61
+		}
62
+	}
63
+	if tc, ok := t.(subtest.TestContext); ok {
64
+		tc.AddCleanup(cleanup)
65
+	}
66
+	return cleanup
67
+}
68
+
69
+// ToMap takes a list of strings in the format returned by os.Environ() and
70
+// returns a mapping of keys to values.
71
+func ToMap(env []string) map[string]string {
72
+	result := map[string]string{}
73
+	for _, raw := range env {
74
+		key, value := getParts(raw)
75
+		result[key] = value
76
+	}
77
+	return result
78
+}
79
+
80
+func getParts(raw string) (string, string) {
81
+	// Environment variables on windows can begin with =
82
+	// http://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx
83
+	parts := strings.SplitN(raw[1:], "=", 2)
84
+	key := raw[:1] + parts[0]
85
+	if len(parts) == 1 {
86
+		return key, ""
87
+	}
88
+	return key, parts[1]
89
+}
90
+
91
+// ChangeWorkingDir to the directory, and return a function which restores the
92
+// previous working directory.
93
+func ChangeWorkingDir(t assert.TestingT, dir string) func() {
94
+	if ht, ok := t.(helperT); ok {
95
+		ht.Helper()
96
+	}
97
+	cwd, err := os.Getwd()
98
+	assert.NilError(t, err)
99
+	assert.NilError(t, os.Chdir(dir))
100
+	cleanup := func() {
101
+		if ht, ok := t.(helperT); ok {
102
+			ht.Helper()
103
+		}
104
+		assert.NilError(t, os.Chdir(cwd))
105
+	}
106
+	if tc, ok := t.(subtest.TestContext); ok {
107
+		tc.AddCleanup(cleanup)
108
+	}
109
+	return cleanup
110
+}
0 111
new file mode 100644
... ...
@@ -0,0 +1,106 @@
0
+/*Package fs provides tools for creating temporary files, and testing the
1
+contents and structure of a directory.
2
+*/
3
+package fs // import "gotest.tools/fs"
4
+
5
+import (
6
+	"io/ioutil"
7
+	"os"
8
+	"path/filepath"
9
+
10
+	"gotest.tools/assert"
11
+	"gotest.tools/x/subtest"
12
+)
13
+
14
+// Path objects return their filesystem path. Path may be implemented by a
15
+// real filesystem object (such as File and Dir) or by a type which updates
16
+// entries in a Manifest.
17
+type Path interface {
18
+	Path() string
19
+	Remove()
20
+}
21
+
22
+var (
23
+	_ Path = &Dir{}
24
+	_ Path = &File{}
25
+)
26
+
27
+// File is a temporary file on the filesystem
28
+type File struct {
29
+	path string
30
+}
31
+
32
+type helperT interface {
33
+	Helper()
34
+}
35
+
36
+// NewFile creates a new file in a temporary directory using prefix as part of
37
+// the filename. The PathOps are applied to the before returning the File.
38
+func NewFile(t assert.TestingT, prefix string, ops ...PathOp) *File {
39
+	if ht, ok := t.(helperT); ok {
40
+		ht.Helper()
41
+	}
42
+	tempfile, err := ioutil.TempFile("", prefix+"-")
43
+	assert.NilError(t, err)
44
+	file := &File{path: tempfile.Name()}
45
+	assert.NilError(t, tempfile.Close())
46
+
47
+	for _, op := range ops {
48
+		assert.NilError(t, op(file))
49
+	}
50
+	if tc, ok := t.(subtest.TestContext); ok {
51
+		tc.AddCleanup(file.Remove)
52
+	}
53
+	return file
54
+}
55
+
56
+// Path returns the full path to the file
57
+func (f *File) Path() string {
58
+	return f.path
59
+}
60
+
61
+// Remove the file
62
+func (f *File) Remove() {
63
+	// nolint: errcheck
64
+	os.Remove(f.path)
65
+}
66
+
67
+// Dir is a temporary directory
68
+type Dir struct {
69
+	path string
70
+}
71
+
72
+// NewDir returns a new temporary directory using prefix as part of the directory
73
+// name. The PathOps are applied before returning the Dir.
74
+func NewDir(t assert.TestingT, prefix string, ops ...PathOp) *Dir {
75
+	if ht, ok := t.(helperT); ok {
76
+		ht.Helper()
77
+	}
78
+	path, err := ioutil.TempDir("", prefix+"-")
79
+	assert.NilError(t, err)
80
+	dir := &Dir{path: path}
81
+
82
+	for _, op := range ops {
83
+		assert.NilError(t, op(dir))
84
+	}
85
+	if tc, ok := t.(subtest.TestContext); ok {
86
+		tc.AddCleanup(dir.Remove)
87
+	}
88
+	return dir
89
+}
90
+
91
+// Path returns the full path to the directory
92
+func (d *Dir) Path() string {
93
+	return d.path
94
+}
95
+
96
+// Remove the directory
97
+func (d *Dir) Remove() {
98
+	// nolint: errcheck
99
+	os.RemoveAll(d.path)
100
+}
101
+
102
+// Join returns a new path with this directory as the base of the path
103
+func (d *Dir) Join(parts ...string) string {
104
+	return filepath.Join(append([]string{d.Path()}, parts...)...)
105
+}
0 106
new file mode 100644
... ...
@@ -0,0 +1,129 @@
0
+package fs
1
+
2
+import (
3
+	"io"
4
+	"io/ioutil"
5
+	"os"
6
+	"path/filepath"
7
+
8
+	"github.com/pkg/errors"
9
+	"gotest.tools/assert"
10
+)
11
+
12
+// Manifest stores the expected structure and properties of files and directories
13
+// in a filesystem.
14
+type Manifest struct {
15
+	root *directory
16
+}
17
+
18
+type resource struct {
19
+	mode os.FileMode
20
+	uid  uint32
21
+	gid  uint32
22
+}
23
+
24
+type file struct {
25
+	resource
26
+	content io.ReadCloser
27
+}
28
+
29
+func (f *file) Type() string {
30
+	return "file"
31
+}
32
+
33
+type symlink struct {
34
+	resource
35
+	target string
36
+}
37
+
38
+func (f *symlink) Type() string {
39
+	return "symlink"
40
+}
41
+
42
+type directory struct {
43
+	resource
44
+	items map[string]dirEntry
45
+}
46
+
47
+func (f *directory) Type() string {
48
+	return "directory"
49
+}
50
+
51
+type dirEntry interface {
52
+	Type() string
53
+}
54
+
55
+// ManifestFromDir creates a Manifest by reading the directory at path. The
56
+// manifest stores the structure and properties of files in the directory.
57
+// ManifestFromDir can be used with Equal to compare two directories.
58
+func ManifestFromDir(t assert.TestingT, path string) Manifest {
59
+	if ht, ok := t.(helperT); ok {
60
+		ht.Helper()
61
+	}
62
+
63
+	manifest, err := manifestFromDir(path)
64
+	assert.NilError(t, err)
65
+	return manifest
66
+}
67
+
68
+func manifestFromDir(path string) (Manifest, error) {
69
+	info, err := os.Stat(path)
70
+	switch {
71
+	case err != nil:
72
+		return Manifest{}, err
73
+	case !info.IsDir():
74
+		return Manifest{}, errors.Errorf("path %s must be a directory", path)
75
+	}
76
+
77
+	directory, err := newDirectory(path, info)
78
+	return Manifest{root: directory}, err
79
+}
80
+
81
+func newDirectory(path string, info os.FileInfo) (*directory, error) {
82
+	items := make(map[string]dirEntry)
83
+	children, err := ioutil.ReadDir(path)
84
+	if err != nil {
85
+		return nil, err
86
+	}
87
+	for _, child := range children {
88
+		fullPath := filepath.Join(path, child.Name())
89
+		items[child.Name()], err = getTypedResource(fullPath, child)
90
+		if err != nil {
91
+			return nil, err
92
+		}
93
+	}
94
+
95
+	return &directory{
96
+		resource: newResourceFromInfo(info),
97
+		items:    items,
98
+	}, nil
99
+}
100
+
101
+func getTypedResource(path string, info os.FileInfo) (dirEntry, error) {
102
+	switch {
103
+	case info.IsDir():
104
+		return newDirectory(path, info)
105
+	case info.Mode()&os.ModeSymlink != 0:
106
+		return newSymlink(path, info)
107
+	// TODO: devices, pipes?
108
+	default:
109
+		return newFile(path, info)
110
+	}
111
+}
112
+
113
+func newSymlink(path string, info os.FileInfo) (*symlink, error) {
114
+	target, err := os.Readlink(path)
115
+	return &symlink{
116
+		resource: newResourceFromInfo(info),
117
+		target:   target,
118
+	}, err
119
+}
120
+
121
+func newFile(path string, info os.FileInfo) (*file, error) {
122
+	// TODO: defer file opening to reduce number of open FDs?
123
+	readCloser, err := os.Open(path)
124
+	return &file{
125
+		resource: newResourceFromInfo(info),
126
+		content:  readCloser,
127
+	}, err
128
+}
0 129
new file mode 100644
... ...
@@ -0,0 +1,30 @@
0
+// +build !windows
1
+
2
+package fs
3
+
4
+import (
5
+	"os"
6
+	"syscall"
7
+)
8
+
9
+const (
10
+	defaultRootDirMode = os.ModeDir | 0700
11
+	defaultSymlinkMode = os.ModeSymlink | 0777
12
+)
13
+
14
+func newResourceFromInfo(info os.FileInfo) resource {
15
+	statT := info.Sys().(*syscall.Stat_t)
16
+	return resource{
17
+		mode: info.Mode(),
18
+		uid:  statT.Uid,
19
+		gid:  statT.Gid,
20
+	}
21
+}
22
+
23
+func (p *filePath) SetMode(mode os.FileMode) {
24
+	p.file.mode = mode
25
+}
26
+
27
+func (p *directoryPath) SetMode(mode os.FileMode) {
28
+	p.directory.mode = mode | os.ModeDir
29
+}
0 30
new file mode 100644
... ...
@@ -0,0 +1,22 @@
0
+package fs
1
+
2
+import "os"
3
+
4
+const (
5
+	defaultRootDirMode = os.ModeDir | 0777
6
+	defaultSymlinkMode = os.ModeSymlink | 0666
7
+)
8
+
9
+func newResourceFromInfo(info os.FileInfo) resource {
10
+	return resource{mode: info.Mode()}
11
+}
12
+
13
+func (p *filePath) SetMode(mode os.FileMode) {
14
+	bits := mode & 0600
15
+	p.file.mode = bits + bits/010 + bits/0100
16
+}
17
+
18
+// TODO: is mode ignored on windows?
19
+func (p *directoryPath) SetMode(mode os.FileMode) {
20
+	p.directory.mode = defaultRootDirMode
21
+}
0 22
new file mode 100644
... ...
@@ -0,0 +1,237 @@
0
+package fs
1
+
2
+import (
3
+	"bytes"
4
+	"io"
5
+	"io/ioutil"
6
+	"os"
7
+	"path/filepath"
8
+	"strings"
9
+	"time"
10
+
11
+	"github.com/pkg/errors"
12
+)
13
+
14
+const defaultFileMode = 0644
15
+
16
+// PathOp is a function which accepts a Path and performs an operation on that
17
+// path. When called with real filesystem objects (File or Dir) a PathOp modifies
18
+// the filesystem at the path. When used with a Manifest object a PathOp updates
19
+// the manifest to expect a value.
20
+type PathOp func(path Path) error
21
+
22
+type manifestResource interface {
23
+	SetMode(mode os.FileMode)
24
+	SetUID(uid uint32)
25
+	SetGID(gid uint32)
26
+}
27
+
28
+type manifestFile interface {
29
+	manifestResource
30
+	SetContent(content io.ReadCloser)
31
+}
32
+
33
+type manifestDirectory interface {
34
+	manifestResource
35
+	AddSymlink(path, target string) error
36
+	AddFile(path string, ops ...PathOp) error
37
+	AddDirectory(path string, ops ...PathOp) error
38
+}
39
+
40
+// WithContent writes content to a file at Path
41
+func WithContent(content string) PathOp {
42
+	return func(path Path) error {
43
+		if m, ok := path.(manifestFile); ok {
44
+			m.SetContent(ioutil.NopCloser(strings.NewReader(content)))
45
+			return nil
46
+		}
47
+		return ioutil.WriteFile(path.Path(), []byte(content), defaultFileMode)
48
+	}
49
+}
50
+
51
+// WithBytes write bytes to a file at Path
52
+func WithBytes(raw []byte) PathOp {
53
+	return func(path Path) error {
54
+		if m, ok := path.(manifestFile); ok {
55
+			m.SetContent(ioutil.NopCloser(bytes.NewReader(raw)))
56
+			return nil
57
+		}
58
+		return ioutil.WriteFile(path.Path(), raw, defaultFileMode)
59
+	}
60
+}
61
+
62
+// AsUser changes ownership of the file system object at Path
63
+func AsUser(uid, gid int) PathOp {
64
+	return func(path Path) error {
65
+		if m, ok := path.(manifestResource); ok {
66
+			m.SetUID(uint32(uid))
67
+			m.SetGID(uint32(gid))
68
+			return nil
69
+		}
70
+		return os.Chown(path.Path(), uid, gid)
71
+	}
72
+}
73
+
74
+// WithFile creates a file in the directory at path with content
75
+func WithFile(filename, content string, ops ...PathOp) PathOp {
76
+	return func(path Path) error {
77
+		if m, ok := path.(manifestDirectory); ok {
78
+			ops = append([]PathOp{WithContent(content), WithMode(defaultFileMode)}, ops...)
79
+			return m.AddFile(filename, ops...)
80
+		}
81
+
82
+		fullpath := filepath.Join(path.Path(), filepath.FromSlash(filename))
83
+		if err := createFile(fullpath, content); err != nil {
84
+			return err
85
+		}
86
+		return applyPathOps(&File{path: fullpath}, ops)
87
+	}
88
+}
89
+
90
+func createFile(fullpath string, content string) error {
91
+	return ioutil.WriteFile(fullpath, []byte(content), defaultFileMode)
92
+}
93
+
94
+// WithFiles creates all the files in the directory at path with their content
95
+func WithFiles(files map[string]string) PathOp {
96
+	return func(path Path) error {
97
+		if m, ok := path.(manifestDirectory); ok {
98
+			for filename, content := range files {
99
+				// TODO: remove duplication with WithFile
100
+				if err := m.AddFile(filename, WithContent(content), WithMode(defaultFileMode)); err != nil {
101
+					return err
102
+				}
103
+			}
104
+			return nil
105
+		}
106
+
107
+		for filename, content := range files {
108
+			fullpath := filepath.Join(path.Path(), filepath.FromSlash(filename))
109
+			if err := createFile(fullpath, content); err != nil {
110
+				return err
111
+			}
112
+		}
113
+		return nil
114
+	}
115
+}
116
+
117
+// FromDir copies the directory tree from the source path into the new Dir
118
+func FromDir(source string) PathOp {
119
+	return func(path Path) error {
120
+		if _, ok := path.(manifestDirectory); ok {
121
+			return errors.New("use manifest.FromDir")
122
+		}
123
+		return copyDirectory(source, path.Path())
124
+	}
125
+}
126
+
127
+// WithDir creates a subdirectory in the directory at path. Additional PathOp
128
+// can be used to modify the subdirectory
129
+func WithDir(name string, ops ...PathOp) PathOp {
130
+	const defaultMode = 0755
131
+	return func(path Path) error {
132
+		if m, ok := path.(manifestDirectory); ok {
133
+			ops = append([]PathOp{WithMode(defaultMode)}, ops...)
134
+			return m.AddDirectory(name, ops...)
135
+		}
136
+
137
+		fullpath := filepath.Join(path.Path(), filepath.FromSlash(name))
138
+		err := os.MkdirAll(fullpath, defaultMode)
139
+		if err != nil {
140
+			return err
141
+		}
142
+		return applyPathOps(&Dir{path: fullpath}, ops)
143
+	}
144
+}
145
+
146
+func applyPathOps(path Path, ops []PathOp) error {
147
+	for _, op := range ops {
148
+		if err := op(path); err != nil {
149
+			return err
150
+		}
151
+	}
152
+	return nil
153
+}
154
+
155
+// WithMode sets the file mode on the directory or file at path
156
+func WithMode(mode os.FileMode) PathOp {
157
+	return func(path Path) error {
158
+		if m, ok := path.(manifestResource); ok {
159
+			m.SetMode(mode)
160
+			return nil
161
+		}
162
+		return os.Chmod(path.Path(), mode)
163
+	}
164
+}
165
+
166
+func copyDirectory(source, dest string) error {
167
+	entries, err := ioutil.ReadDir(source)
168
+	if err != nil {
169
+		return err
170
+	}
171
+	for _, entry := range entries {
172
+		sourcePath := filepath.Join(source, entry.Name())
173
+		destPath := filepath.Join(dest, entry.Name())
174
+		if entry.IsDir() {
175
+			if err := os.Mkdir(destPath, 0755); err != nil {
176
+				return err
177
+			}
178
+			if err := copyDirectory(sourcePath, destPath); err != nil {
179
+				return err
180
+			}
181
+			continue
182
+		}
183
+		// TODO: handle symlinks
184
+		if err := copyFile(sourcePath, destPath); err != nil {
185
+			return err
186
+		}
187
+	}
188
+	return nil
189
+}
190
+
191
+func copyFile(source, dest string) error {
192
+	content, err := ioutil.ReadFile(source)
193
+	if err != nil {
194
+		return err
195
+	}
196
+	return ioutil.WriteFile(dest, content, 0644)
197
+}
198
+
199
+// WithSymlink creates a symlink in the directory which links to target.
200
+// Target must be a path relative to the directory.
201
+//
202
+// Note: the argument order is the inverse of os.Symlink to be consistent with
203
+// the other functions in this package.
204
+func WithSymlink(path, target string) PathOp {
205
+	return func(root Path) error {
206
+		if v, ok := root.(manifestDirectory); ok {
207
+			return v.AddSymlink(path, target)
208
+		}
209
+		return os.Symlink(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path))
210
+	}
211
+}
212
+
213
+// WithHardlink creates a link in the directory which links to target.
214
+// Target must be a path relative to the directory.
215
+//
216
+// Note: the argument order is the inverse of os.Link to be consistent with
217
+// the other functions in this package.
218
+func WithHardlink(path, target string) PathOp {
219
+	return func(root Path) error {
220
+		if _, ok := root.(manifestDirectory); ok {
221
+			return errors.New("WithHardlink yet implemented for manifests")
222
+		}
223
+		return os.Link(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path))
224
+	}
225
+}
226
+
227
+// WithTimestamps sets the access and modification times of the file system object
228
+// at path.
229
+func WithTimestamps(atime, mtime time.Time) PathOp {
230
+	return func(root Path) error {
231
+		if _, ok := root.(manifestDirectory); ok {
232
+			return errors.New("WithTimestamp yet implemented for manifests")
233
+		}
234
+		return os.Chtimes(root.Path(), atime, mtime)
235
+	}
236
+}
0 237
new file mode 100644
... ...
@@ -0,0 +1,151 @@
0
+package fs
1
+
2
+import (
3
+	"bytes"
4
+	"io"
5
+	"io/ioutil"
6
+	"os"
7
+
8
+	"gotest.tools/assert"
9
+)
10
+
11
+// resourcePath is an adaptor for resources so they can be used as a Path
12
+// with PathOps.
13
+type resourcePath struct{}
14
+
15
+func (p *resourcePath) Path() string {
16
+	return "manifest: not a filesystem path"
17
+}
18
+
19
+func (p *resourcePath) Remove() {}
20
+
21
+type filePath struct {
22
+	resourcePath
23
+	file *file
24
+}
25
+
26
+func (p *filePath) SetContent(content io.ReadCloser) {
27
+	p.file.content = content
28
+}
29
+
30
+func (p *filePath) SetUID(uid uint32) {
31
+	p.file.uid = uid
32
+}
33
+
34
+func (p *filePath) SetGID(gid uint32) {
35
+	p.file.gid = gid
36
+}
37
+
38
+type directoryPath struct {
39
+	resourcePath
40
+	directory *directory
41
+}
42
+
43
+func (p *directoryPath) SetUID(uid uint32) {
44
+	p.directory.uid = uid
45
+}
46
+
47
+func (p *directoryPath) SetGID(gid uint32) {
48
+	p.directory.gid = gid
49
+}
50
+
51
+func (p *directoryPath) AddSymlink(path, target string) error {
52
+	p.directory.items[path] = &symlink{
53
+		resource: newResource(defaultSymlinkMode),
54
+		target:   target,
55
+	}
56
+	return nil
57
+}
58
+
59
+func (p *directoryPath) AddFile(path string, ops ...PathOp) error {
60
+	newFile := &file{resource: newResource(0)}
61
+	p.directory.items[path] = newFile
62
+	exp := &filePath{file: newFile}
63
+	return applyPathOps(exp, ops)
64
+}
65
+
66
+func (p *directoryPath) AddDirectory(path string, ops ...PathOp) error {
67
+	newDir := newDirectoryWithDefaults()
68
+	p.directory.items[path] = newDir
69
+	exp := &directoryPath{directory: newDir}
70
+	return applyPathOps(exp, ops)
71
+}
72
+
73
+// Expected returns a Manifest with a directory structured created by ops. The
74
+// PathOp operations are applied to the manifest as expectations of the
75
+// filesystem structure and properties.
76
+func Expected(t assert.TestingT, ops ...PathOp) Manifest {
77
+	if ht, ok := t.(helperT); ok {
78
+		ht.Helper()
79
+	}
80
+
81
+	newDir := newDirectoryWithDefaults()
82
+	e := &directoryPath{directory: newDir}
83
+	assert.NilError(t, applyPathOps(e, ops))
84
+	return Manifest{root: newDir}
85
+}
86
+
87
+func newDirectoryWithDefaults() *directory {
88
+	return &directory{
89
+		resource: newResource(defaultRootDirMode),
90
+		items:    make(map[string]dirEntry),
91
+	}
92
+}
93
+
94
+func newResource(mode os.FileMode) resource {
95
+	return resource{
96
+		mode: mode,
97
+		uid:  currentUID(),
98
+		gid:  currentGID(),
99
+	}
100
+}
101
+
102
+func currentUID() uint32 {
103
+	return normalizeID(os.Getuid())
104
+}
105
+
106
+func currentGID() uint32 {
107
+	return normalizeID(os.Getgid())
108
+}
109
+
110
+func normalizeID(id int) uint32 {
111
+	// ids will be -1 on windows
112
+	if id < 0 {
113
+		return 0
114
+	}
115
+	return uint32(id)
116
+}
117
+
118
+var anyFileContent = ioutil.NopCloser(bytes.NewReader(nil))
119
+
120
+// MatchAnyFileContent is a PathOp that updates a Manifest so that the file
121
+// at path may contain any content.
122
+func MatchAnyFileContent(path Path) error {
123
+	if m, ok := path.(*filePath); ok {
124
+		m.SetContent(anyFileContent)
125
+	}
126
+	return nil
127
+}
128
+
129
+const anyFile = "*"
130
+
131
+// MatchExtraFiles is a PathOp that updates a Manifest to allow a directory
132
+// to contain unspecified files.
133
+func MatchExtraFiles(path Path) error {
134
+	if m, ok := path.(*directoryPath); ok {
135
+		m.AddFile(anyFile)
136
+	}
137
+	return nil
138
+}
139
+
140
+// anyFileMode is represented by uint32_max
141
+const anyFileMode os.FileMode = 4294967295
142
+
143
+// MatchAnyFileMode is a PathOp that updates a Manifest so that the resource at path
144
+// will match any file mode.
145
+func MatchAnyFileMode(path Path) error {
146
+	if m, ok := path.(manifestResource); ok {
147
+		m.SetMode(anyFileMode)
148
+	}
149
+	return nil
150
+}
0 151
new file mode 100644
... ...
@@ -0,0 +1,215 @@
0
+package fs
1
+
2
+import (
3
+	"bytes"
4
+	"fmt"
5
+	"io/ioutil"
6
+	"os"
7
+	"path/filepath"
8
+	"sort"
9
+	"strings"
10
+
11
+	"gotest.tools/assert/cmp"
12
+	"gotest.tools/internal/format"
13
+)
14
+
15
+// Equal compares a directory to the expected structured described by a manifest
16
+// and returns success if they match. If they do not match the failure message
17
+// will contain all the differences between the directory structure and the
18
+// expected structure defined by the Manifest.
19
+//
20
+// Equal is a cmp.Comparison which can be used with assert.Assert().
21
+func Equal(path string, expected Manifest) cmp.Comparison {
22
+	return func() cmp.Result {
23
+		actual, err := manifestFromDir(path)
24
+		if err != nil {
25
+			return cmp.ResultFromError(err)
26
+		}
27
+		failures := eqDirectory(string(os.PathSeparator), expected.root, actual.root)
28
+		if len(failures) == 0 {
29
+			return cmp.ResultSuccess
30
+		}
31
+		msg := fmt.Sprintf("directory %s does not match expected:\n", path)
32
+		return cmp.ResultFailure(msg + formatFailures(failures))
33
+	}
34
+}
35
+
36
+type failure struct {
37
+	path     string
38
+	problems []problem
39
+}
40
+
41
+type problem string
42
+
43
+func notEqual(property string, x, y interface{}) problem {
44
+	return problem(fmt.Sprintf("%s: expected %s got %s", property, x, y))
45
+}
46
+
47
+func errProblem(reason string, err error) problem {
48
+	return problem(fmt.Sprintf("%s: %s", reason, err))
49
+}
50
+
51
+func existenceProblem(filename, reason string, args ...interface{}) problem {
52
+	return problem(filename + ": " + fmt.Sprintf(reason, args...))
53
+}
54
+
55
+func eqResource(x, y resource) []problem {
56
+	var p []problem
57
+	if x.uid != y.uid {
58
+		p = append(p, notEqual("uid", x.uid, y.uid))
59
+	}
60
+	if x.gid != y.gid {
61
+		p = append(p, notEqual("gid", x.gid, y.gid))
62
+	}
63
+	if x.mode != anyFileMode && x.mode != y.mode {
64
+		p = append(p, notEqual("mode", x.mode, y.mode))
65
+	}
66
+	return p
67
+}
68
+
69
+func eqFile(x, y *file) []problem {
70
+	p := eqResource(x.resource, y.resource)
71
+
72
+	switch {
73
+	case x.content == nil:
74
+		p = append(p, existenceProblem("content", "expected content is nil"))
75
+		return p
76
+	case x.content == anyFileContent:
77
+		return p
78
+	case y.content == nil:
79
+		p = append(p, existenceProblem("content", "actual content is nil"))
80
+		return p
81
+	}
82
+
83
+	xContent, xErr := ioutil.ReadAll(x.content)
84
+	defer x.content.Close()
85
+	yContent, yErr := ioutil.ReadAll(y.content)
86
+	defer y.content.Close()
87
+
88
+	if xErr != nil {
89
+		p = append(p, errProblem("failed to read expected content", xErr))
90
+	}
91
+	if yErr != nil {
92
+		p = append(p, errProblem("failed to read actual content", xErr))
93
+	}
94
+	if xErr != nil || yErr != nil {
95
+		return p
96
+	}
97
+
98
+	if !bytes.Equal(xContent, yContent) {
99
+		p = append(p, diffContent(xContent, yContent))
100
+	}
101
+	return p
102
+}
103
+
104
+func diffContent(x, y []byte) problem {
105
+	diff := format.UnifiedDiff(format.DiffConfig{
106
+		A:    string(x),
107
+		B:    string(y),
108
+		From: "expected",
109
+		To:   "actual",
110
+	})
111
+	// Remove the trailing newline in the diff. A trailing newline is always
112
+	// added to a problem by formatFailures.
113
+	diff = strings.TrimSuffix(diff, "\n")
114
+	return problem("content:\n" + indent(diff, "    "))
115
+}
116
+
117
+func indent(s, prefix string) string {
118
+	buf := new(bytes.Buffer)
119
+	lines := strings.SplitAfter(s, "\n")
120
+	for _, line := range lines {
121
+		buf.WriteString(prefix + line)
122
+	}
123
+	return buf.String()
124
+}
125
+
126
+func eqSymlink(x, y *symlink) []problem {
127
+	p := eqResource(x.resource, y.resource)
128
+	if x.target != y.target {
129
+		p = append(p, notEqual("target", x.target, y.target))
130
+	}
131
+	return p
132
+}
133
+
134
+func eqDirectory(path string, x, y *directory) []failure {
135
+	p := eqResource(x.resource, y.resource)
136
+	var f []failure
137
+
138
+	for _, name := range sortedKeys(x.items) {
139
+		if name == anyFile {
140
+			continue
141
+		}
142
+		xEntry := x.items[name]
143
+		yEntry, ok := y.items[name]
144
+		if !ok {
145
+			p = append(p, existenceProblem(name, "expected %s to exist", xEntry.Type()))
146
+			continue
147
+		}
148
+
149
+		if xEntry.Type() != yEntry.Type() {
150
+			p = append(p, notEqual(name, xEntry.Type(), yEntry.Type()))
151
+			continue
152
+		}
153
+
154
+		f = append(f, eqEntry(filepath.Join(path, name), xEntry, yEntry)...)
155
+	}
156
+
157
+	if _, ok := x.items[anyFile]; !ok {
158
+		for _, name := range sortedKeys(y.items) {
159
+			if _, ok := x.items[name]; !ok {
160
+				yEntry := y.items[name]
161
+				p = append(p, existenceProblem(name, "unexpected %s", yEntry.Type()))
162
+			}
163
+		}
164
+	}
165
+
166
+	if len(p) > 0 {
167
+		f = append(f, failure{path: path, problems: p})
168
+	}
169
+	return f
170
+}
171
+
172
+func sortedKeys(items map[string]dirEntry) []string {
173
+	var keys []string
174
+	for key := range items {
175
+		keys = append(keys, key)
176
+	}
177
+	sort.Strings(keys)
178
+	return keys
179
+}
180
+
181
+// eqEntry assumes x and y to be the same type
182
+func eqEntry(path string, x, y dirEntry) []failure {
183
+	resp := func(problems []problem) []failure {
184
+		if len(problems) == 0 {
185
+			return nil
186
+		}
187
+		return []failure{{path: path, problems: problems}}
188
+	}
189
+
190
+	switch typed := x.(type) {
191
+	case *file:
192
+		return resp(eqFile(typed, y.(*file)))
193
+	case *symlink:
194
+		return resp(eqSymlink(typed, y.(*symlink)))
195
+	case *directory:
196
+		return eqDirectory(path, typed, y.(*directory))
197
+	}
198
+	return nil
199
+}
200
+
201
+func formatFailures(failures []failure) string {
202
+	sort.Slice(failures, func(i, j int) bool {
203
+		return failures[i].path < failures[j].path
204
+	})
205
+
206
+	buf := new(bytes.Buffer)
207
+	for _, failure := range failures {
208
+		buf.WriteString(failure.path + "\n")
209
+		for _, problem := range failure.problems {
210
+			buf.WriteString("  " + string(problem) + "\n")
211
+		}
212
+	}
213
+	return buf.String()
214
+}
0 215
new file mode 100644
... ...
@@ -0,0 +1,284 @@
0
+/*Package icmd executes binaries and provides convenient assertions for testing the results.
1
+ */
2
+package icmd // import "gotest.tools/icmd"
3
+
4
+import (
5
+	"bytes"
6
+	"fmt"
7
+	"io"
8
+	"os/exec"
9
+	"strings"
10
+	"sync"
11
+	"time"
12
+
13
+	"gotest.tools/assert"
14
+	"gotest.tools/assert/cmp"
15
+)
16
+
17
+type helperT interface {
18
+	Helper()
19
+}
20
+
21
+// None is a token to inform Result.Assert that the output should be empty
22
+const None = "[NOTHING]"
23
+
24
+type lockedBuffer struct {
25
+	m   sync.RWMutex
26
+	buf bytes.Buffer
27
+}
28
+
29
+func (buf *lockedBuffer) Write(b []byte) (int, error) {
30
+	buf.m.Lock()
31
+	defer buf.m.Unlock()
32
+	return buf.buf.Write(b)
33
+}
34
+
35
+func (buf *lockedBuffer) String() string {
36
+	buf.m.RLock()
37
+	defer buf.m.RUnlock()
38
+	return buf.buf.String()
39
+}
40
+
41
+// Result stores the result of running a command
42
+type Result struct {
43
+	Cmd      *exec.Cmd
44
+	ExitCode int
45
+	Error    error
46
+	// Timeout is true if the command was killed because it ran for too long
47
+	Timeout   bool
48
+	outBuffer *lockedBuffer
49
+	errBuffer *lockedBuffer
50
+}
51
+
52
+// Assert compares the Result against the Expected struct, and fails the test if
53
+// any of the expectations are not met.
54
+//
55
+// This function is equivalent to assert.Assert(t, result.Equal(exp)).
56
+func (r *Result) Assert(t assert.TestingT, exp Expected) *Result {
57
+	if ht, ok := t.(helperT); ok {
58
+		ht.Helper()
59
+	}
60
+	assert.Assert(t, r.Equal(exp))
61
+	return r
62
+}
63
+
64
+// Equal compares the result to Expected. If the result doesn't match expected
65
+// returns a formatted failure message with the command, stdout, stderr, exit code,
66
+// and any failed expectations.
67
+func (r *Result) Equal(exp Expected) cmp.Comparison {
68
+	return func() cmp.Result {
69
+		return cmp.ResultFromError(r.match(exp))
70
+	}
71
+}
72
+
73
+// Compare the result to Expected and return an error if they do not match.
74
+func (r *Result) Compare(exp Expected) error {
75
+	return r.match(exp)
76
+}
77
+
78
+// nolint: gocyclo
79
+func (r *Result) match(exp Expected) error {
80
+	errors := []string{}
81
+	add := func(format string, args ...interface{}) {
82
+		errors = append(errors, fmt.Sprintf(format, args...))
83
+	}
84
+
85
+	if exp.ExitCode != r.ExitCode {
86
+		add("ExitCode was %d expected %d", r.ExitCode, exp.ExitCode)
87
+	}
88
+	if exp.Timeout != r.Timeout {
89
+		if exp.Timeout {
90
+			add("Expected command to timeout")
91
+		} else {
92
+			add("Expected command to finish, but it hit the timeout")
93
+		}
94
+	}
95
+	if !matchOutput(exp.Out, r.Stdout()) {
96
+		add("Expected stdout to contain %q", exp.Out)
97
+	}
98
+	if !matchOutput(exp.Err, r.Stderr()) {
99
+		add("Expected stderr to contain %q", exp.Err)
100
+	}
101
+	switch {
102
+	// If a non-zero exit code is expected there is going to be an error.
103
+	// Don't require an error message as well as an exit code because the
104
+	// error message is going to be "exit status <code> which is not useful
105
+	case exp.Error == "" && exp.ExitCode != 0:
106
+	case exp.Error == "" && r.Error != nil:
107
+		add("Expected no error")
108
+	case exp.Error != "" && r.Error == nil:
109
+		add("Expected error to contain %q, but there was no error", exp.Error)
110
+	case exp.Error != "" && !strings.Contains(r.Error.Error(), exp.Error):
111
+		add("Expected error to contain %q", exp.Error)
112
+	}
113
+
114
+	if len(errors) == 0 {
115
+		return nil
116
+	}
117
+	return fmt.Errorf("%s\nFailures:\n%s", r, strings.Join(errors, "\n"))
118
+}
119
+
120
+func matchOutput(expected string, actual string) bool {
121
+	switch expected {
122
+	case None:
123
+		return actual == ""
124
+	default:
125
+		return strings.Contains(actual, expected)
126
+	}
127
+}
128
+
129
+func (r *Result) String() string {
130
+	var timeout string
131
+	if r.Timeout {
132
+		timeout = " (timeout)"
133
+	}
134
+
135
+	return fmt.Sprintf(`
136
+Command:  %s
137
+ExitCode: %d%s
138
+Error:    %v
139
+Stdout:   %v
140
+Stderr:   %v
141
+`,
142
+		strings.Join(r.Cmd.Args, " "),
143
+		r.ExitCode,
144
+		timeout,
145
+		r.Error,
146
+		r.Stdout(),
147
+		r.Stderr())
148
+}
149
+
150
+// Expected is the expected output from a Command. This struct is compared to a
151
+// Result struct by Result.Assert().
152
+type Expected struct {
153
+	ExitCode int
154
+	Timeout  bool
155
+	Error    string
156
+	Out      string
157
+	Err      string
158
+}
159
+
160
+// Success is the default expected result. A Success result is one with a 0
161
+// ExitCode.
162
+var Success = Expected{}
163
+
164
+// Stdout returns the stdout of the process as a string
165
+func (r *Result) Stdout() string {
166
+	return r.outBuffer.String()
167
+}
168
+
169
+// Stderr returns the stderr of the process as a string
170
+func (r *Result) Stderr() string {
171
+	return r.errBuffer.String()
172
+}
173
+
174
+// Combined returns the stdout and stderr combined into a single string
175
+func (r *Result) Combined() string {
176
+	return r.outBuffer.String() + r.errBuffer.String()
177
+}
178
+
179
+func (r *Result) setExitError(err error) {
180
+	if err == nil {
181
+		return
182
+	}
183
+	r.Error = err
184
+	r.ExitCode = processExitCode(err)
185
+}
186
+
187
+// Cmd contains the arguments and options for a process to run as part of a test
188
+// suite.
189
+type Cmd struct {
190
+	Command []string
191
+	Timeout time.Duration
192
+	Stdin   io.Reader
193
+	Stdout  io.Writer
194
+	Dir     string
195
+	Env     []string
196
+}
197
+
198
+// Command create a simple Cmd with the specified command and arguments
199
+func Command(command string, args ...string) Cmd {
200
+	return Cmd{Command: append([]string{command}, args...)}
201
+}
202
+
203
+// RunCmd runs a command and returns a Result
204
+func RunCmd(cmd Cmd, cmdOperators ...CmdOp) *Result {
205
+	for _, op := range cmdOperators {
206
+		op(&cmd)
207
+	}
208
+	result := StartCmd(cmd)
209
+	if result.Error != nil {
210
+		return result
211
+	}
212
+	return WaitOnCmd(cmd.Timeout, result)
213
+}
214
+
215
+// RunCommand runs a command with default options, and returns a result
216
+func RunCommand(command string, args ...string) *Result {
217
+	return RunCmd(Command(command, args...))
218
+}
219
+
220
+// StartCmd starts a command, but doesn't wait for it to finish
221
+func StartCmd(cmd Cmd) *Result {
222
+	result := buildCmd(cmd)
223
+	if result.Error != nil {
224
+		return result
225
+	}
226
+	result.setExitError(result.Cmd.Start())
227
+	return result
228
+}
229
+
230
+// TODO: support exec.CommandContext
231
+func buildCmd(cmd Cmd) *Result {
232
+	var execCmd *exec.Cmd
233
+	switch len(cmd.Command) {
234
+	case 1:
235
+		execCmd = exec.Command(cmd.Command[0])
236
+	default:
237
+		execCmd = exec.Command(cmd.Command[0], cmd.Command[1:]...)
238
+	}
239
+	outBuffer := new(lockedBuffer)
240
+	errBuffer := new(lockedBuffer)
241
+
242
+	execCmd.Stdin = cmd.Stdin
243
+	execCmd.Dir = cmd.Dir
244
+	execCmd.Env = cmd.Env
245
+	if cmd.Stdout != nil {
246
+		execCmd.Stdout = io.MultiWriter(outBuffer, cmd.Stdout)
247
+	} else {
248
+		execCmd.Stdout = outBuffer
249
+	}
250
+	execCmd.Stderr = errBuffer
251
+	return &Result{
252
+		Cmd:       execCmd,
253
+		outBuffer: outBuffer,
254
+		errBuffer: errBuffer,
255
+	}
256
+}
257
+
258
+// WaitOnCmd waits for a command to complete. If timeout is non-nil then
259
+// only wait until the timeout.
260
+func WaitOnCmd(timeout time.Duration, result *Result) *Result {
261
+	if timeout == time.Duration(0) {
262
+		result.setExitError(result.Cmd.Wait())
263
+		return result
264
+	}
265
+
266
+	done := make(chan error, 1)
267
+	// Wait for command to exit in a goroutine
268
+	go func() {
269
+		done <- result.Cmd.Wait()
270
+	}()
271
+
272
+	select {
273
+	case <-time.After(timeout):
274
+		killErr := result.Cmd.Process.Kill()
275
+		if killErr != nil {
276
+			fmt.Printf("failed to kill (pid=%d): %v\n", result.Cmd.Process.Pid, killErr)
277
+		}
278
+		result.Timeout = true
279
+	case err := <-done:
280
+		result.setExitError(err)
281
+	}
282
+	return result
283
+}
0 284
new file mode 100644
... ...
@@ -0,0 +1,32 @@
0
+package icmd
1
+
2
+import (
3
+	"os/exec"
4
+	"syscall"
5
+
6
+	"github.com/pkg/errors"
7
+)
8
+
9
+// getExitCode returns the ExitStatus of a process from the error returned by
10
+// exec.Run(). If the exit status could not be parsed an error is returned.
11
+func getExitCode(err error) (int, error) {
12
+	if exiterr, ok := err.(*exec.ExitError); ok {
13
+		if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok {
14
+			return procExit.ExitStatus(), nil
15
+		}
16
+	}
17
+	return 0, errors.Wrap(err, "failed to get exit code")
18
+}
19
+
20
+func processExitCode(err error) (exitCode int) {
21
+	if err == nil {
22
+		return 0
23
+	}
24
+	exitCode, exiterr := getExitCode(err)
25
+	if exiterr != nil {
26
+		// TODO: Fix this so we check the error's text.
27
+		// we've failed to retrieve exit code, so we set it to 127
28
+		return 127
29
+	}
30
+	return exitCode
31
+}
0 32
new file mode 100644
... ...
@@ -0,0 +1,4 @@
0
+package icmd
1
+
2
+// CmdOp is an operation which modified a Cmd structure used to execute commands
3
+type CmdOp func(*Cmd)
0 4
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+Copyright (c) 2013, Patrick Mezard
1
+All rights reserved.
2
+
3
+Redistribution and use in source and binary forms, with or without
4
+modification, are permitted provided that the following conditions are
5
+met:
6
+
7
+    Redistributions of source code must retain the above copyright
8
+notice, this list of conditions and the following disclaimer.
9
+    Redistributions in binary form must reproduce the above copyright
10
+notice, this list of conditions and the following disclaimer in the
11
+documentation and/or other materials provided with the distribution.
12
+    The names of its contributors may not be used to endorse or promote
13
+products derived from this software without specific prior written
14
+permission.
15
+
16
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
17
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
22
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0 27
new file mode 100644
... ...
@@ -0,0 +1,420 @@
0
+/* Package difflib is a partial port of Python difflib module.
1
+
2
+Original source: https://github.com/pmezard/go-difflib
3
+
4
+This file is trimmed to only the parts used by this repository.
5
+*/
6
+package difflib // import "gotest.tools/internal/difflib"
7
+
8
+func min(a, b int) int {
9
+	if a < b {
10
+		return a
11
+	}
12
+	return b
13
+}
14
+
15
+func max(a, b int) int {
16
+	if a > b {
17
+		return a
18
+	}
19
+	return b
20
+}
21
+
22
+type Match struct {
23
+	A    int
24
+	B    int
25
+	Size int
26
+}
27
+
28
+type OpCode struct {
29
+	Tag byte
30
+	I1  int
31
+	I2  int
32
+	J1  int
33
+	J2  int
34
+}
35
+
36
+// SequenceMatcher compares sequence of strings. The basic
37
+// algorithm predates, and is a little fancier than, an algorithm
38
+// published in the late 1980's by Ratcliff and Obershelp under the
39
+// hyperbolic name "gestalt pattern matching".  The basic idea is to find
40
+// the longest contiguous matching subsequence that contains no "junk"
41
+// elements (R-O doesn't address junk).  The same idea is then applied
42
+// recursively to the pieces of the sequences to the left and to the right
43
+// of the matching subsequence.  This does not yield minimal edit
44
+// sequences, but does tend to yield matches that "look right" to people.
45
+//
46
+// SequenceMatcher tries to compute a "human-friendly diff" between two
47
+// sequences.  Unlike e.g. UNIX(tm) diff, the fundamental notion is the
48
+// longest *contiguous* & junk-free matching subsequence.  That's what
49
+// catches peoples' eyes.  The Windows(tm) windiff has another interesting
50
+// notion, pairing up elements that appear uniquely in each sequence.
51
+// That, and the method here, appear to yield more intuitive difference
52
+// reports than does diff.  This method appears to be the least vulnerable
53
+// to synching up on blocks of "junk lines", though (like blank lines in
54
+// ordinary text files, or maybe "<P>" lines in HTML files).  That may be
55
+// because this is the only method of the 3 that has a *concept* of
56
+// "junk" <wink>.
57
+//
58
+// Timing:  Basic R-O is cubic time worst case and quadratic time expected
59
+// case.  SequenceMatcher is quadratic time for the worst case and has
60
+// expected-case behavior dependent in a complicated way on how many
61
+// elements the sequences have in common; best case time is linear.
62
+type SequenceMatcher struct {
63
+	a              []string
64
+	b              []string
65
+	b2j            map[string][]int
66
+	IsJunk         func(string) bool
67
+	autoJunk       bool
68
+	bJunk          map[string]struct{}
69
+	matchingBlocks []Match
70
+	fullBCount     map[string]int
71
+	bPopular       map[string]struct{}
72
+	opCodes        []OpCode
73
+}
74
+
75
+func NewMatcher(a, b []string) *SequenceMatcher {
76
+	m := SequenceMatcher{autoJunk: true}
77
+	m.SetSeqs(a, b)
78
+	return &m
79
+}
80
+
81
+// Set two sequences to be compared.
82
+func (m *SequenceMatcher) SetSeqs(a, b []string) {
83
+	m.SetSeq1(a)
84
+	m.SetSeq2(b)
85
+}
86
+
87
+// Set the first sequence to be compared. The second sequence to be compared is
88
+// not changed.
89
+//
90
+// SequenceMatcher computes and caches detailed information about the second
91
+// sequence, so if you want to compare one sequence S against many sequences,
92
+// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other
93
+// sequences.
94
+//
95
+// See also SetSeqs() and SetSeq2().
96
+func (m *SequenceMatcher) SetSeq1(a []string) {
97
+	if &a == &m.a {
98
+		return
99
+	}
100
+	m.a = a
101
+	m.matchingBlocks = nil
102
+	m.opCodes = nil
103
+}
104
+
105
+// Set the second sequence to be compared. The first sequence to be compared is
106
+// not changed.
107
+func (m *SequenceMatcher) SetSeq2(b []string) {
108
+	if &b == &m.b {
109
+		return
110
+	}
111
+	m.b = b
112
+	m.matchingBlocks = nil
113
+	m.opCodes = nil
114
+	m.fullBCount = nil
115
+	m.chainB()
116
+}
117
+
118
+func (m *SequenceMatcher) chainB() {
119
+	// Populate line -> index mapping
120
+	b2j := map[string][]int{}
121
+	for i, s := range m.b {
122
+		indices := b2j[s]
123
+		indices = append(indices, i)
124
+		b2j[s] = indices
125
+	}
126
+
127
+	// Purge junk elements
128
+	m.bJunk = map[string]struct{}{}
129
+	if m.IsJunk != nil {
130
+		junk := m.bJunk
131
+		for s, _ := range b2j {
132
+			if m.IsJunk(s) {
133
+				junk[s] = struct{}{}
134
+			}
135
+		}
136
+		for s, _ := range junk {
137
+			delete(b2j, s)
138
+		}
139
+	}
140
+
141
+	// Purge remaining popular elements
142
+	popular := map[string]struct{}{}
143
+	n := len(m.b)
144
+	if m.autoJunk && n >= 200 {
145
+		ntest := n/100 + 1
146
+		for s, indices := range b2j {
147
+			if len(indices) > ntest {
148
+				popular[s] = struct{}{}
149
+			}
150
+		}
151
+		for s, _ := range popular {
152
+			delete(b2j, s)
153
+		}
154
+	}
155
+	m.bPopular = popular
156
+	m.b2j = b2j
157
+}
158
+
159
+func (m *SequenceMatcher) isBJunk(s string) bool {
160
+	_, ok := m.bJunk[s]
161
+	return ok
162
+}
163
+
164
+// Find longest matching block in a[alo:ahi] and b[blo:bhi].
165
+//
166
+// If IsJunk is not defined:
167
+//
168
+// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
169
+//     alo <= i <= i+k <= ahi
170
+//     blo <= j <= j+k <= bhi
171
+// and for all (i',j',k') meeting those conditions,
172
+//     k >= k'
173
+//     i <= i'
174
+//     and if i == i', j <= j'
175
+//
176
+// In other words, of all maximal matching blocks, return one that
177
+// starts earliest in a, and of all those maximal matching blocks that
178
+// start earliest in a, return the one that starts earliest in b.
179
+//
180
+// If IsJunk is defined, first the longest matching block is
181
+// determined as above, but with the additional restriction that no
182
+// junk element appears in the block.  Then that block is extended as
183
+// far as possible by matching (only) junk elements on both sides.  So
184
+// the resulting block never matches on junk except as identical junk
185
+// happens to be adjacent to an "interesting" match.
186
+//
187
+// If no blocks match, return (alo, blo, 0).
188
+func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
189
+	// CAUTION:  stripping common prefix or suffix would be incorrect.
190
+	// E.g.,
191
+	//    ab
192
+	//    acab
193
+	// Longest matching block is "ab", but if common prefix is
194
+	// stripped, it's "a" (tied with "b").  UNIX(tm) diff does so
195
+	// strip, so ends up claiming that ab is changed to acab by
196
+	// inserting "ca" in the middle.  That's minimal but unintuitive:
197
+	// "it's obvious" that someone inserted "ac" at the front.
198
+	// Windiff ends up at the same place as diff, but by pairing up
199
+	// the unique 'b's and then matching the first two 'a's.
200
+	besti, bestj, bestsize := alo, blo, 0
201
+
202
+	// find longest junk-free match
203
+	// during an iteration of the loop, j2len[j] = length of longest
204
+	// junk-free match ending with a[i-1] and b[j]
205
+	j2len := map[int]int{}
206
+	for i := alo; i != ahi; i++ {
207
+		// look at all instances of a[i] in b; note that because
208
+		// b2j has no junk keys, the loop is skipped if a[i] is junk
209
+		newj2len := map[int]int{}
210
+		for _, j := range m.b2j[m.a[i]] {
211
+			// a[i] matches b[j]
212
+			if j < blo {
213
+				continue
214
+			}
215
+			if j >= bhi {
216
+				break
217
+			}
218
+			k := j2len[j-1] + 1
219
+			newj2len[j] = k
220
+			if k > bestsize {
221
+				besti, bestj, bestsize = i-k+1, j-k+1, k
222
+			}
223
+		}
224
+		j2len = newj2len
225
+	}
226
+
227
+	// Extend the best by non-junk elements on each end.  In particular,
228
+	// "popular" non-junk elements aren't in b2j, which greatly speeds
229
+	// the inner loop above, but also means "the best" match so far
230
+	// doesn't contain any junk *or* popular non-junk elements.
231
+	for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) &&
232
+		m.a[besti-1] == m.b[bestj-1] {
233
+		besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
234
+	}
235
+	for besti+bestsize < ahi && bestj+bestsize < bhi &&
236
+		!m.isBJunk(m.b[bestj+bestsize]) &&
237
+		m.a[besti+bestsize] == m.b[bestj+bestsize] {
238
+		bestsize += 1
239
+	}
240
+
241
+	// Now that we have a wholly interesting match (albeit possibly
242
+	// empty!), we may as well suck up the matching junk on each
243
+	// side of it too.  Can't think of a good reason not to, and it
244
+	// saves post-processing the (possibly considerable) expense of
245
+	// figuring out what to do with it.  In the case of an empty
246
+	// interesting match, this is clearly the right thing to do,
247
+	// because no other kind of match is possible in the regions.
248
+	for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) &&
249
+		m.a[besti-1] == m.b[bestj-1] {
250
+		besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
251
+	}
252
+	for besti+bestsize < ahi && bestj+bestsize < bhi &&
253
+		m.isBJunk(m.b[bestj+bestsize]) &&
254
+		m.a[besti+bestsize] == m.b[bestj+bestsize] {
255
+		bestsize += 1
256
+	}
257
+
258
+	return Match{A: besti, B: bestj, Size: bestsize}
259
+}
260
+
261
+// Return list of triples describing matching subsequences.
262
+//
263
+// Each triple is of the form (i, j, n), and means that
264
+// a[i:i+n] == b[j:j+n].  The triples are monotonically increasing in
265
+// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are
266
+// adjacent triples in the list, and the second is not the last triple in the
267
+// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe
268
+// adjacent equal blocks.
269
+//
270
+// The last triple is a dummy, (len(a), len(b), 0), and is the only
271
+// triple with n==0.
272
+func (m *SequenceMatcher) GetMatchingBlocks() []Match {
273
+	if m.matchingBlocks != nil {
274
+		return m.matchingBlocks
275
+	}
276
+
277
+	var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match
278
+	matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {
279
+		match := m.findLongestMatch(alo, ahi, blo, bhi)
280
+		i, j, k := match.A, match.B, match.Size
281
+		if match.Size > 0 {
282
+			if alo < i && blo < j {
283
+				matched = matchBlocks(alo, i, blo, j, matched)
284
+			}
285
+			matched = append(matched, match)
286
+			if i+k < ahi && j+k < bhi {
287
+				matched = matchBlocks(i+k, ahi, j+k, bhi, matched)
288
+			}
289
+		}
290
+		return matched
291
+	}
292
+	matched := matchBlocks(0, len(m.a), 0, len(m.b), nil)
293
+
294
+	// It's possible that we have adjacent equal blocks in the
295
+	// matching_blocks list now.
296
+	nonAdjacent := []Match{}
297
+	i1, j1, k1 := 0, 0, 0
298
+	for _, b := range matched {
299
+		// Is this block adjacent to i1, j1, k1?
300
+		i2, j2, k2 := b.A, b.B, b.Size
301
+		if i1+k1 == i2 && j1+k1 == j2 {
302
+			// Yes, so collapse them -- this just increases the length of
303
+			// the first block by the length of the second, and the first
304
+			// block so lengthened remains the block to compare against.
305
+			k1 += k2
306
+		} else {
307
+			// Not adjacent.  Remember the first block (k1==0 means it's
308
+			// the dummy we started with), and make the second block the
309
+			// new block to compare against.
310
+			if k1 > 0 {
311
+				nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
312
+			}
313
+			i1, j1, k1 = i2, j2, k2
314
+		}
315
+	}
316
+	if k1 > 0 {
317
+		nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
318
+	}
319
+
320
+	nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})
321
+	m.matchingBlocks = nonAdjacent
322
+	return m.matchingBlocks
323
+}
324
+
325
+// Return list of 5-tuples describing how to turn a into b.
326
+//
327
+// Each tuple is of the form (tag, i1, i2, j1, j2).  The first tuple
328
+// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
329
+// tuple preceding it, and likewise for j1 == the previous j2.
330
+//
331
+// The tags are characters, with these meanings:
332
+//
333
+// 'r' (replace):  a[i1:i2] should be replaced by b[j1:j2]
334
+//
335
+// 'd' (delete):   a[i1:i2] should be deleted, j1==j2 in this case.
336
+//
337
+// 'i' (insert):   b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.
338
+//
339
+// 'e' (equal):    a[i1:i2] == b[j1:j2]
340
+func (m *SequenceMatcher) GetOpCodes() []OpCode {
341
+	if m.opCodes != nil {
342
+		return m.opCodes
343
+	}
344
+	i, j := 0, 0
345
+	matching := m.GetMatchingBlocks()
346
+	opCodes := make([]OpCode, 0, len(matching))
347
+	for _, m := range matching {
348
+		//  invariant:  we've pumped out correct diffs to change
349
+		//  a[:i] into b[:j], and the next matching block is
350
+		//  a[ai:ai+size] == b[bj:bj+size]. So we need to pump
351
+		//  out a diff to change a[i:ai] into b[j:bj], pump out
352
+		//  the matching block, and move (i,j) beyond the match
353
+		ai, bj, size := m.A, m.B, m.Size
354
+		tag := byte(0)
355
+		if i < ai && j < bj {
356
+			tag = 'r'
357
+		} else if i < ai {
358
+			tag = 'd'
359
+		} else if j < bj {
360
+			tag = 'i'
361
+		}
362
+		if tag > 0 {
363
+			opCodes = append(opCodes, OpCode{tag, i, ai, j, bj})
364
+		}
365
+		i, j = ai+size, bj+size
366
+		// the list of matching blocks is terminated by a
367
+		// sentinel with size 0
368
+		if size > 0 {
369
+			opCodes = append(opCodes, OpCode{'e', ai, i, bj, j})
370
+		}
371
+	}
372
+	m.opCodes = opCodes
373
+	return m.opCodes
374
+}
375
+
376
+// Isolate change clusters by eliminating ranges with no changes.
377
+//
378
+// Return a generator of groups with up to n lines of context.
379
+// Each group is in the same format as returned by GetOpCodes().
380
+func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
381
+	if n < 0 {
382
+		n = 3
383
+	}
384
+	codes := m.GetOpCodes()
385
+	if len(codes) == 0 {
386
+		codes = []OpCode{OpCode{'e', 0, 1, 0, 1}}
387
+	}
388
+	// Fixup leading and trailing groups if they show no changes.
389
+	if codes[0].Tag == 'e' {
390
+		c := codes[0]
391
+		i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
392
+		codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
393
+	}
394
+	if codes[len(codes)-1].Tag == 'e' {
395
+		c := codes[len(codes)-1]
396
+		i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
397
+		codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
398
+	}
399
+	nn := n + n
400
+	groups := [][]OpCode{}
401
+	group := []OpCode{}
402
+	for _, c := range codes {
403
+		i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
404
+		// End the current group and start a new one whenever
405
+		// there is a large range with no changes.
406
+		if c.Tag == 'e' && i2-i1 > nn {
407
+			group = append(group, OpCode{c.Tag, i1, min(i2, i1+n),
408
+				j1, min(j2, j1+n)})
409
+			groups = append(groups, group)
410
+			group = []OpCode{}
411
+			i1, j1 = max(i1, i2-n), max(j1, j2-n)
412
+		}
413
+		group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
414
+	}
415
+	if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
416
+		groups = append(groups, group)
417
+	}
418
+	return groups
419
+}
0 420
new file mode 100644
... ...
@@ -0,0 +1,161 @@
0
+package format
1
+
2
+import (
3
+	"bytes"
4
+	"fmt"
5
+	"strings"
6
+	"unicode"
7
+
8
+	"gotest.tools/internal/difflib"
9
+)
10
+
11
+const (
12
+	contextLines = 2
13
+)
14
+
15
+// DiffConfig for a unified diff
16
+type DiffConfig struct {
17
+	A    string
18
+	B    string
19
+	From string
20
+	To   string
21
+}
22
+
23
+// UnifiedDiff is a modified version of difflib.WriteUnifiedDiff with better
24
+// support for showing the whitespace differences.
25
+func UnifiedDiff(conf DiffConfig) string {
26
+	a := strings.SplitAfter(conf.A, "\n")
27
+	b := strings.SplitAfter(conf.B, "\n")
28
+	groups := difflib.NewMatcher(a, b).GetGroupedOpCodes(contextLines)
29
+	if len(groups) == 0 {
30
+		return ""
31
+	}
32
+
33
+	buf := new(bytes.Buffer)
34
+	writeFormat := func(format string, args ...interface{}) {
35
+		buf.WriteString(fmt.Sprintf(format, args...))
36
+	}
37
+	writeLine := func(prefix string, s string) {
38
+		buf.WriteString(prefix + s)
39
+	}
40
+	if hasWhitespaceDiffLines(groups, a, b) {
41
+		writeLine = visibleWhitespaceLine(writeLine)
42
+	}
43
+	formatHeader(writeFormat, conf)
44
+	for _, group := range groups {
45
+		formatRangeLine(writeFormat, group)
46
+		for _, opCode := range group {
47
+			in, out := a[opCode.I1:opCode.I2], b[opCode.J1:opCode.J2]
48
+			switch opCode.Tag {
49
+			case 'e':
50
+				formatLines(writeLine, " ", in)
51
+			case 'r':
52
+				formatLines(writeLine, "-", in)
53
+				formatLines(writeLine, "+", out)
54
+			case 'd':
55
+				formatLines(writeLine, "-", in)
56
+			case 'i':
57
+				formatLines(writeLine, "+", out)
58
+			}
59
+		}
60
+	}
61
+	return buf.String()
62
+}
63
+
64
+// hasWhitespaceDiffLines returns true if any diff groups is only different
65
+// because of whitespace characters.
66
+func hasWhitespaceDiffLines(groups [][]difflib.OpCode, a, b []string) bool {
67
+	for _, group := range groups {
68
+		in, out := new(bytes.Buffer), new(bytes.Buffer)
69
+		for _, opCode := range group {
70
+			if opCode.Tag == 'e' {
71
+				continue
72
+			}
73
+			for _, line := range a[opCode.I1:opCode.I2] {
74
+				in.WriteString(line)
75
+			}
76
+			for _, line := range b[opCode.J1:opCode.J2] {
77
+				out.WriteString(line)
78
+			}
79
+		}
80
+		if removeWhitespace(in.String()) == removeWhitespace(out.String()) {
81
+			return true
82
+		}
83
+	}
84
+	return false
85
+}
86
+
87
+func removeWhitespace(s string) string {
88
+	var result []rune
89
+	for _, r := range s {
90
+		if !unicode.IsSpace(r) {
91
+			result = append(result, r)
92
+		}
93
+	}
94
+	return string(result)
95
+}
96
+
97
+func visibleWhitespaceLine(ws func(string, string)) func(string, string) {
98
+	mapToVisibleSpace := func(r rune) rune {
99
+		switch r {
100
+		case '\n':
101
+		case ' ':
102
+			return '·'
103
+		case '\t':
104
+			return '▷'
105
+		case '\v':
106
+			return '▽'
107
+		case '\r':
108
+			return '↵'
109
+		case '\f':
110
+			return '↓'
111
+		default:
112
+			if unicode.IsSpace(r) {
113
+				return '�'
114
+			}
115
+		}
116
+		return r
117
+	}
118
+	return func(prefix, s string) {
119
+		ws(prefix, strings.Map(mapToVisibleSpace, s))
120
+	}
121
+}
122
+
123
+func formatHeader(wf func(string, ...interface{}), conf DiffConfig) {
124
+	if conf.From != "" || conf.To != "" {
125
+		wf("--- %s\n", conf.From)
126
+		wf("+++ %s\n", conf.To)
127
+	}
128
+}
129
+
130
+func formatRangeLine(wf func(string, ...interface{}), group []difflib.OpCode) {
131
+	first, last := group[0], group[len(group)-1]
132
+	range1 := formatRangeUnified(first.I1, last.I2)
133
+	range2 := formatRangeUnified(first.J1, last.J2)
134
+	wf("@@ -%s +%s @@\n", range1, range2)
135
+}
136
+
137
+// Convert range to the "ed" format
138
+func formatRangeUnified(start, stop int) string {
139
+	// Per the diff spec at http://www.unix.org/single_unix_specification/
140
+	beginning := start + 1 // lines start numbering with one
141
+	length := stop - start
142
+	if length == 1 {
143
+		return fmt.Sprintf("%d", beginning)
144
+	}
145
+	if length == 0 {
146
+		beginning-- // empty ranges begin at line just before the range
147
+	}
148
+	return fmt.Sprintf("%d,%d", beginning, length)
149
+}
150
+
151
+func formatLines(writeLine func(string, string), prefix string, lines []string) {
152
+	for _, line := range lines {
153
+		writeLine(prefix, line)
154
+	}
155
+	// Add a newline if the last line is missing one so that the diff displays
156
+	// properly.
157
+	if !strings.HasSuffix(lines[len(lines)-1], "\n") {
158
+		writeLine("", "\n")
159
+	}
160
+}
0 161
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+package format // import "gotest.tools/internal/format"
1
+
2
+import "fmt"
3
+
4
+// Message accepts a msgAndArgs varargs and formats it using fmt.Sprintf
5
+func Message(msgAndArgs ...interface{}) string {
6
+	switch len(msgAndArgs) {
7
+	case 0:
8
+		return ""
9
+	case 1:
10
+		return fmt.Sprintf("%v", msgAndArgs[0])
11
+	default:
12
+		return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
13
+	}
14
+}
15
+
16
+// WithCustomMessage accepts one or two messages and formats them appropriately
17
+func WithCustomMessage(source string, msgAndArgs ...interface{}) string {
18
+	custom := Message(msgAndArgs...)
19
+	switch {
20
+	case custom == "":
21
+		return source
22
+	case source == "":
23
+		return custom
24
+	}
25
+	return fmt.Sprintf("%s: %s", source, custom)
26
+}
0 27
new file mode 100644
... ...
@@ -0,0 +1,163 @@
0
+package source // import "gotest.tools/internal/source"
1
+
2
+import (
3
+	"bytes"
4
+	"fmt"
5
+	"go/ast"
6
+	"go/format"
7
+	"go/parser"
8
+	"go/token"
9
+	"os"
10
+	"runtime"
11
+	"strconv"
12
+	"strings"
13
+
14
+	"github.com/pkg/errors"
15
+)
16
+
17
+const baseStackIndex = 1
18
+
19
+// FormattedCallExprArg returns the argument from an ast.CallExpr at the
20
+// index in the call stack. The argument is formatted using FormatNode.
21
+func FormattedCallExprArg(stackIndex int, argPos int) (string, error) {
22
+	args, err := CallExprArgs(stackIndex + 1)
23
+	if err != nil {
24
+		return "", err
25
+	}
26
+	return FormatNode(args[argPos])
27
+}
28
+
29
+func getNodeAtLine(filename string, lineNum int) (ast.Node, error) {
30
+	fileset := token.NewFileSet()
31
+	astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors)
32
+	if err != nil {
33
+		return nil, errors.Wrapf(err, "failed to parse source file: %s", filename)
34
+	}
35
+
36
+	node := scanToLine(fileset, astFile, lineNum)
37
+	if node == nil {
38
+		return nil, errors.Errorf(
39
+			"failed to find an expression on line %d in %s", lineNum, filename)
40
+	}
41
+	return node, nil
42
+}
43
+
44
+func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node {
45
+	v := &scanToLineVisitor{lineNum: lineNum, fileset: fileset}
46
+	ast.Walk(v, node)
47
+	return v.matchedNode
48
+}
49
+
50
+type scanToLineVisitor struct {
51
+	lineNum     int
52
+	matchedNode ast.Node
53
+	fileset     *token.FileSet
54
+}
55
+
56
+func (v *scanToLineVisitor) Visit(node ast.Node) ast.Visitor {
57
+	if node == nil || v.matchedNode != nil {
58
+		return nil
59
+	}
60
+	if v.nodePosition(node).Line == v.lineNum {
61
+		v.matchedNode = node
62
+		return nil
63
+	}
64
+	return v
65
+}
66
+
67
+// In golang 1.9 the line number changed from being the line where the statement
68
+// ended to the line where the statement began.
69
+func (v *scanToLineVisitor) nodePosition(node ast.Node) token.Position {
70
+	if goVersionBefore19 {
71
+		return v.fileset.Position(node.End())
72
+	}
73
+	return v.fileset.Position(node.Pos())
74
+}
75
+
76
+var goVersionBefore19 = isGOVersionBefore19()
77
+
78
+func isGOVersionBefore19() bool {
79
+	version := runtime.Version()
80
+	// not a release version
81
+	if !strings.HasPrefix(version, "go") {
82
+		return false
83
+	}
84
+	version = strings.TrimPrefix(version, "go")
85
+	parts := strings.Split(version, ".")
86
+	if len(parts) < 2 {
87
+		return false
88
+	}
89
+	minor, err := strconv.ParseInt(parts[1], 10, 32)
90
+	return err == nil && parts[0] == "1" && minor < 9
91
+}
92
+
93
+func getCallExprArgs(node ast.Node) ([]ast.Expr, error) {
94
+	visitor := &callExprVisitor{}
95
+	ast.Walk(visitor, node)
96
+	if visitor.expr == nil {
97
+		return nil, errors.New("failed to find call expression")
98
+	}
99
+	return visitor.expr.Args, nil
100
+}
101
+
102
+type callExprVisitor struct {
103
+	expr *ast.CallExpr
104
+}
105
+
106
+func (v *callExprVisitor) Visit(node ast.Node) ast.Visitor {
107
+	if v.expr != nil || node == nil {
108
+		return nil
109
+	}
110
+	debug("visit (%T): %s", node, debugFormatNode{node})
111
+
112
+	if callExpr, ok := node.(*ast.CallExpr); ok {
113
+		v.expr = callExpr
114
+		return nil
115
+	}
116
+	return v
117
+}
118
+
119
+// FormatNode using go/format.Node and return the result as a string
120
+func FormatNode(node ast.Node) (string, error) {
121
+	buf := new(bytes.Buffer)
122
+	err := format.Node(buf, token.NewFileSet(), node)
123
+	return buf.String(), err
124
+}
125
+
126
+// CallExprArgs returns the ast.Expr slice for the args of an ast.CallExpr at
127
+// the index in the call stack.
128
+func CallExprArgs(stackIndex int) ([]ast.Expr, error) {
129
+	_, filename, lineNum, ok := runtime.Caller(baseStackIndex + stackIndex)
130
+	if !ok {
131
+		return nil, errors.New("failed to get call stack")
132
+	}
133
+	debug("call stack position: %s:%d", filename, lineNum)
134
+
135
+	node, err := getNodeAtLine(filename, lineNum)
136
+	if err != nil {
137
+		return nil, err
138
+	}
139
+	debug("found node (%T): %s", node, debugFormatNode{node})
140
+
141
+	return getCallExprArgs(node)
142
+}
143
+
144
+var debugEnabled = os.Getenv("GOTESTYOURSELF_DEBUG") != ""
145
+
146
+func debug(format string, args ...interface{}) {
147
+	if debugEnabled {
148
+		fmt.Fprintf(os.Stderr, "DEBUG: "+format+"\n", args...)
149
+	}
150
+}
151
+
152
+type debugFormatNode struct {
153
+	ast.Node
154
+}
155
+
156
+func (n debugFormatNode) String() string {
157
+	out, err := FormatNode(n.Node)
158
+	if err != nil {
159
+		return fmt.Sprintf("failed to format %s: %s", n.Node, err)
160
+	}
161
+	return out
162
+}
0 163
new file mode 100644
... ...
@@ -0,0 +1,140 @@
0
+/*Package poll provides tools for testing asynchronous code.
1
+ */
2
+package poll // import "gotest.tools/poll"
3
+
4
+import (
5
+	"fmt"
6
+	"time"
7
+)
8
+
9
+// TestingT is the subset of testing.T used by WaitOn
10
+type TestingT interface {
11
+	LogT
12
+	Fatalf(format string, args ...interface{})
13
+}
14
+
15
+// LogT is a logging interface that is passed to the WaitOn check function
16
+type LogT interface {
17
+	Log(args ...interface{})
18
+	Logf(format string, args ...interface{})
19
+}
20
+
21
+type helperT interface {
22
+	Helper()
23
+}
24
+
25
+// Settings are used to configure the behaviour of WaitOn
26
+type Settings struct {
27
+	// Timeout is the maximum time to wait for the condition. Defaults to 10s.
28
+	Timeout time.Duration
29
+	// Delay is the time to sleep between checking the condition. Defaults to
30
+	// 100ms.
31
+	Delay time.Duration
32
+}
33
+
34
+func defaultConfig() *Settings {
35
+	return &Settings{Timeout: 10 * time.Second, Delay: 100 * time.Millisecond}
36
+}
37
+
38
+// SettingOp is a function which accepts and modifies Settings
39
+type SettingOp func(config *Settings)
40
+
41
+// WithDelay sets the delay to wait between polls
42
+func WithDelay(delay time.Duration) SettingOp {
43
+	return func(config *Settings) {
44
+		config.Delay = delay
45
+	}
46
+}
47
+
48
+// WithTimeout sets the timeout
49
+func WithTimeout(timeout time.Duration) SettingOp {
50
+	return func(config *Settings) {
51
+		config.Timeout = timeout
52
+	}
53
+}
54
+
55
+// Result of a check performed by WaitOn
56
+type Result interface {
57
+	// Error indicates that the check failed and polling should stop, and the
58
+	// the has failed
59
+	Error() error
60
+	// Done indicates that polling should stop, and the test should proceed
61
+	Done() bool
62
+	// Message provides the most recent state when polling has not completed
63
+	Message() string
64
+}
65
+
66
+type result struct {
67
+	done    bool
68
+	message string
69
+	err     error
70
+}
71
+
72
+func (r result) Done() bool {
73
+	return r.done
74
+}
75
+
76
+func (r result) Message() string {
77
+	return r.message
78
+}
79
+
80
+func (r result) Error() error {
81
+	return r.err
82
+}
83
+
84
+// Continue returns a Result that indicates to WaitOn that it should continue
85
+// polling. The message text will be used as the failure message if the timeout
86
+// is reached.
87
+func Continue(message string, args ...interface{}) Result {
88
+	return result{message: fmt.Sprintf(message, args...)}
89
+}
90
+
91
+// Success returns a Result where Done() returns true, which indicates to WaitOn
92
+// that it should stop polling and exit without an error.
93
+func Success() Result {
94
+	return result{done: true}
95
+}
96
+
97
+// Error returns a Result that indicates to WaitOn that it should fail the test
98
+// and stop polling.
99
+func Error(err error) Result {
100
+	return result{err: err}
101
+}
102
+
103
+// WaitOn a condition or until a timeout. Poll by calling check and exit when
104
+// check returns a done Result. To fail a test and exit polling with an error
105
+// return a error result.
106
+func WaitOn(t TestingT, check func(t LogT) Result, pollOps ...SettingOp) {
107
+	if ht, ok := t.(helperT); ok {
108
+		ht.Helper()
109
+	}
110
+	config := defaultConfig()
111
+	for _, pollOp := range pollOps {
112
+		pollOp(config)
113
+	}
114
+
115
+	var lastMessage string
116
+	after := time.After(config.Timeout)
117
+	chResult := make(chan Result)
118
+	for {
119
+		go func() {
120
+			chResult <- check(t)
121
+		}()
122
+		select {
123
+		case <-after:
124
+			if lastMessage == "" {
125
+				lastMessage = "first check never completed"
126
+			}
127
+			t.Fatalf("timeout hit after %s: %s", config.Timeout, lastMessage)
128
+		case result := <-chResult:
129
+			switch {
130
+			case result.Error() != nil:
131
+				t.Fatalf("polling check failed: %s", result.Error())
132
+			case result.Done():
133
+				return
134
+			}
135
+			time.Sleep(config.Delay)
136
+			lastMessage = result.Message()
137
+		}
138
+	}
139
+}
0 140
new file mode 100644
... ...
@@ -0,0 +1,71 @@
0
+/*Package skip provides functions for skipping a test and printing the source code
1
+of the condition used to skip the test.
2
+*/
3
+package skip // import "gotest.tools/skip"
4
+
5
+import (
6
+	"fmt"
7
+	"path"
8
+	"reflect"
9
+	"runtime"
10
+	"strings"
11
+
12
+	"gotest.tools/internal/format"
13
+	"gotest.tools/internal/source"
14
+)
15
+
16
+type skipT interface {
17
+	Skip(args ...interface{})
18
+	Log(args ...interface{})
19
+}
20
+
21
+type helperT interface {
22
+	Helper()
23
+}
24
+
25
+// BoolOrCheckFunc can be a bool or func() bool, other types will panic
26
+type BoolOrCheckFunc interface{}
27
+
28
+// If the condition expression evaluates to true, or the condition function returns
29
+// true, skip the test.
30
+// The skip message will contain the source code of the expression.
31
+// Extra message text can be passed as a format string with args
32
+func If(t skipT, condition BoolOrCheckFunc, msgAndArgs ...interface{}) {
33
+	if ht, ok := t.(helperT); ok {
34
+		ht.Helper()
35
+	}
36
+	switch check := condition.(type) {
37
+	case bool:
38
+		ifCondition(t, check, msgAndArgs...)
39
+	case func() bool:
40
+		if check() {
41
+			t.Skip(format.WithCustomMessage(getFunctionName(check), msgAndArgs...))
42
+		}
43
+	default:
44
+		panic(fmt.Sprintf("invalid type for condition arg: %T", check))
45
+	}
46
+}
47
+
48
+func getFunctionName(function func() bool) string {
49
+	funcPath := runtime.FuncForPC(reflect.ValueOf(function).Pointer()).Name()
50
+	return strings.SplitN(path.Base(funcPath), ".", 2)[1]
51
+}
52
+
53
+func ifCondition(t skipT, condition bool, msgAndArgs ...interface{}) {
54
+	if ht, ok := t.(helperT); ok {
55
+		ht.Helper()
56
+	}
57
+	if !condition {
58
+		return
59
+	}
60
+	const (
61
+		stackIndex = 2
62
+		argPos     = 1
63
+	)
64
+	source, err := source.FormattedCallExprArg(stackIndex, argPos)
65
+	if err != nil {
66
+		t.Log(err.Error())
67
+		t.Skip(format.Message(msgAndArgs...))
68
+	}
69
+	t.Skip(format.WithCustomMessage(source, msgAndArgs...))
70
+}
0 71
new file mode 100644
... ...
@@ -0,0 +1,81 @@
0
+/*Package subtest provides a TestContext to subtests which handles cleanup, and
1
+provides a testing.TB, and context.Context.
2
+
3
+This package was inspired by github.com/frankban/quicktest.
4
+*/
5
+package subtest // import "gotest.tools/x/subtest"
6
+
7
+import (
8
+	"context"
9
+	"testing"
10
+)
11
+
12
+type testcase struct {
13
+	testing.TB
14
+	ctx          context.Context
15
+	cleanupFuncs []cleanupFunc
16
+}
17
+
18
+type cleanupFunc func()
19
+
20
+func (tc *testcase) Ctx() context.Context {
21
+	if tc.ctx == nil {
22
+		var cancel func()
23
+		tc.ctx, cancel = context.WithCancel(context.Background())
24
+		tc.AddCleanup(cancel)
25
+	}
26
+	return tc.ctx
27
+}
28
+
29
+// Cleanup runs all cleanup functions. Functions are run in the opposite order
30
+// in which they were added. Cleanup is called automatically before Run exits.
31
+func (tc *testcase) Cleanup() {
32
+	for _, f := range tc.cleanupFuncs {
33
+		// Defer all cleanup functions so they all run even if one calls
34
+		// t.FailNow() or panics. Deferring them also runs them in reverse order.
35
+		defer f()
36
+	}
37
+	tc.cleanupFuncs = nil
38
+}
39
+
40
+func (tc *testcase) AddCleanup(f func()) {
41
+	tc.cleanupFuncs = append(tc.cleanupFuncs, f)
42
+}
43
+
44
+func (tc *testcase) Parallel() {
45
+	tp, ok := tc.TB.(parallel)
46
+	if !ok {
47
+		panic("Parallel called with a testing.B")
48
+	}
49
+	tp.Parallel()
50
+}
51
+
52
+type parallel interface {
53
+	Parallel()
54
+}
55
+
56
+// Run a subtest. When subtest exits, every cleanup function added with
57
+// TestContext.AddCleanup will be run.
58
+func Run(t *testing.T, name string, subtest func(t TestContext)) bool {
59
+	return t.Run(name, func(t *testing.T) {
60
+		tc := &testcase{TB: t}
61
+		defer tc.Cleanup()
62
+		subtest(tc)
63
+	})
64
+}
65
+
66
+// TestContext provides a testing.TB and a context.Context for a test case.
67
+type TestContext interface {
68
+	testing.TB
69
+	// AddCleanup function which will be run when before Run returns.
70
+	AddCleanup(f func())
71
+	// Ctx returns a context for the test case. Multiple calls from the same subtest
72
+	// will return the same context. The context is cancelled when Run
73
+	// returns.
74
+	Ctx() context.Context
75
+	// Parallel calls t.Parallel on the testing.TB. Panics if testing.TB does
76
+	// not implement Parallel.
77
+	Parallel()
78
+}
79
+
80
+var _ TestContext = &testcase{}