3 |
4 | Permission to use, copy, modify, and/or distribute this software for any purpose
5 | with or without fee is hereby granted, provided that the above copyright notice
6 | and this permission notice appear in all copies.
7 |
8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
10 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
12 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
13 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
14 | THIS SOFTWARE.
15 | */
16 |
17 | package resize
18 |
19 | import (
20 | "image"
21 | )
22 |
23 | // Thumbnail will downscale provided image to max width and height preserving
24 | // original aspect ratio and using the interpolation function interp.
25 | // It will return original image, without processing it, if original sizes
26 | // are already smaller than provided constraints.
27 | func Thumbnail(maxWidth, maxHeight uint, img image.Image, interp InterpolationFunction) image.Image {
28 | origBounds := img.Bounds()
29 | origWidth := uint(origBounds.Dx())
30 | origHeight := uint(origBounds.Dy())
31 | newWidth, newHeight := origWidth, origHeight
32 |
33 | // Return original image if it have same or smaller size as constraints
34 | if maxWidth >= origWidth && maxHeight >= origHeight {
35 | return img
36 | }
37 |
38 | // Preserve aspect ratio
39 | if origWidth > maxWidth {
40 | newHeight = uint(origHeight * maxWidth / origWidth)
41 | if newHeight < 1 {
42 | newHeight = 1
43 | }
44 | newWidth = maxWidth
45 | }
46 |
47 | if newHeight > maxHeight {
48 | newWidth = uint(newWidth * maxHeight / newHeight)
49 | if newWidth < 1 {
50 | newWidth = 1
51 | }
52 | newHeight = maxHeight
53 | }
54 | return Resize(newWidth, newHeight, img, interp)
55 | }
56 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/nfnt/resize/thumbnail_test.go:
--------------------------------------------------------------------------------
1 | package resize
2 |
3 | import (
4 | "image"
5 | "runtime"
6 | "testing"
7 | )
8 |
9 | func init() {
10 | runtime.GOMAXPROCS(runtime.NumCPU())
11 | }
12 |
13 | var thumbnailTests = []struct {
14 | origWidth int
15 | origHeight int
16 | maxWidth uint
17 | maxHeight uint
18 | expectedWidth uint
19 | expectedHeight uint
20 | }{
21 | {5, 5, 10, 10, 5, 5},
22 | {10, 10, 5, 5, 5, 5},
23 | {10, 50, 10, 10, 2, 10},
24 | {50, 10, 10, 10, 10, 2},
25 | {50, 100, 60, 90, 45, 90},
26 | {120, 100, 60, 90, 60, 50},
27 | {200, 250, 200, 150, 120, 150},
28 | }
29 |
30 | func TestThumbnail(t *testing.T) {
31 | for i, tt := range thumbnailTests {
32 | img := image.NewGray16(image.Rect(0, 0, tt.origWidth, tt.origHeight))
33 |
34 | outImg := Thumbnail(tt.maxWidth, tt.maxHeight, img, NearestNeighbor)
35 |
36 | newWidth := uint(outImg.Bounds().Dx())
37 | newHeight := uint(outImg.Bounds().Dy())
38 | if newWidth != tt.expectedWidth ||
39 | newHeight != tt.expectedHeight {
40 | t.Errorf("%d. Thumbnail(%v, %v, img, NearestNeighbor) => "+
41 | "width: %v, height: %v, want width: %v, height: %v",
42 | i, tt.maxWidth, tt.maxHeight,
43 | newWidth, newHeight, tt.expectedWidth, tt.expectedHeight,
44 | )
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 |
6 | # Folders
7 | _obj
8 | _test
9 |
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 |
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | _testmain.go
21 |
22 | *.exe
23 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/LICENSE.md:
--------------------------------------------------------------------------------
1 | objx - by Mat Ryer and Tyler Bunnell
2 |
3 | The MIT License (MIT)
4 |
5 | Copyright (c) 2014 Stretchr, Inc.
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in all
15 | copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | SOFTWARE.
24 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/README.md:
--------------------------------------------------------------------------------
1 | # objx
2 |
3 | * Jump into the [API Documentation](http://godoc.org/github.com/stretchr/objx)
4 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/codegen/array-access.txt:
--------------------------------------------------------------------------------
1 | case []{1}:
2 | a := object.([]{1})
3 | if isSet {
4 | a[index] = value.({1})
5 | } else {
6 | if index >= len(a) {
7 | if panics {
8 | panic(fmt.Sprintf("objx: Index %d is out of range because the []{1} only contains %d items.", index, len(a)))
9 | }
10 | return nil
11 | } else {
12 | return a[index]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/codegen/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Codegen
5 |
6 |
17 |
18 |
19 |
20 |
21 | Template
22 |
23 |
24 | Use {x}
as a placeholder for each argument.
25 |
26 |
27 |
28 |
29 | Arguments (comma separated)
30 |
31 |
32 | One block per line
33 |
34 |
35 |
36 |
37 | Output
38 |
39 |
40 |
41 |
42 |
43 |
44 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/codegen/types_list.txt:
--------------------------------------------------------------------------------
1 | Interface,interface{},"something",nil,Inter
2 | Map,map[string]interface{},map[string]interface{}{"name":"Tyler"},nil,MSI
3 | ObjxMap,(Map),New(1),New(nil),ObjxMap
4 | Bool,bool,true,false,Bool
5 | String,string,"hello","",Str
6 | Int,int,1,0,Int
7 | Int8,int8,1,0,Int8
8 | Int16,int16,1,0,Int16
9 | Int32,int32,1,0,Int32
10 | Int64,int64,1,0,Int64
11 | Uint,uint,1,0,Uint
12 | Uint8,uint8,1,0,Uint8
13 | Uint16,uint16,1,0,Uint16
14 | Uint32,uint32,1,0,Uint32
15 | Uint64,uint64,1,0,Uint64
16 | Uintptr,uintptr,1,0,Uintptr
17 | Float32,float32,1,0,Float32
18 | Float64,float64,1,0,Float64
19 | Complex64,complex64,1,0,Complex64
20 | Complex128,complex128,1,0,Complex128
21 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/constants.go:
--------------------------------------------------------------------------------
1 | package objx
2 |
3 | const (
4 | // PathSeparator is the character used to separate the elements
5 | // of the keypath.
6 | //
7 | // For example, `location.address.city`
8 | PathSeparator string = "."
9 |
10 | // SignatureSeparator is the character that is used to
11 | // separate the Base64 string from the security signature.
12 | SignatureSeparator = "_"
13 | )
14 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/conversions.go:
--------------------------------------------------------------------------------
1 | package objx
2 |
3 | import (
4 | "bytes"
5 | "encoding/base64"
6 | "encoding/json"
7 | "errors"
8 | "fmt"
9 | "net/url"
10 | )
11 |
12 | // JSON converts the contained object to a JSON string
13 | // representation
14 | func (m Map) JSON() (string, error) {
15 |
16 | result, err := json.Marshal(m)
17 |
18 | if err != nil {
19 | err = errors.New("objx: JSON encode failed with: " + err.Error())
20 | }
21 |
22 | return string(result), err
23 |
24 | }
25 |
26 | // MustJSON converts the contained object to a JSON string
27 | // representation and panics if there is an error
28 | func (m Map) MustJSON() string {
29 | result, err := m.JSON()
30 | if err != nil {
31 | panic(err.Error())
32 | }
33 | return result
34 | }
35 |
36 | // Base64 converts the contained object to a Base64 string
37 | // representation of the JSON string representation
38 | func (m Map) Base64() (string, error) {
39 |
40 | var buf bytes.Buffer
41 |
42 | jsonData, err := m.JSON()
43 | if err != nil {
44 | return "", err
45 | }
46 |
47 | encoder := base64.NewEncoder(base64.StdEncoding, &buf)
48 | encoder.Write([]byte(jsonData))
49 | encoder.Close()
50 |
51 | return buf.String(), nil
52 |
53 | }
54 |
55 | // MustBase64 converts the contained object to a Base64 string
56 | // representation of the JSON string representation and panics
57 | // if there is an error
58 | func (m Map) MustBase64() string {
59 | result, err := m.Base64()
60 | if err != nil {
61 | panic(err.Error())
62 | }
63 | return result
64 | }
65 |
66 | // SignedBase64 converts the contained object to a Base64 string
67 | // representation of the JSON string representation and signs it
68 | // using the provided key.
69 | func (m Map) SignedBase64(key string) (string, error) {
70 |
71 | base64, err := m.Base64()
72 | if err != nil {
73 | return "", err
74 | }
75 |
76 | sig := HashWithKey(base64, key)
77 |
78 | return base64 + SignatureSeparator + sig, nil
79 |
80 | }
81 |
82 | // MustSignedBase64 converts the contained object to a Base64 string
83 | // representation of the JSON string representation and signs it
84 | // using the provided key and panics if there is an error
85 | func (m Map) MustSignedBase64(key string) string {
86 | result, err := m.SignedBase64(key)
87 | if err != nil {
88 | panic(err.Error())
89 | }
90 | return result
91 | }
92 |
93 | /*
94 | URL Query
95 | ------------------------------------------------
96 | */
97 |
98 | // URLValues creates a url.Values object from an Obj. This
99 | // function requires that the wrapped object be a map[string]interface{}
100 | func (m Map) URLValues() url.Values {
101 |
102 | vals := make(url.Values)
103 |
104 | for k, v := range m {
105 | //TODO: can this be done without sprintf?
106 | vals.Set(k, fmt.Sprintf("%v", v))
107 | }
108 |
109 | return vals
110 | }
111 |
112 | // URLQuery gets an encoded URL query representing the given
113 | // Obj. This function requires that the wrapped object be a
114 | // map[string]interface{}
115 | func (m Map) URLQuery() (string, error) {
116 | return m.URLValues().Encode(), nil
117 | }
118 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/conversions_test.go:
--------------------------------------------------------------------------------
1 | package objx
2 |
3 | import (
4 | "github.com/stretchr/testify/assert"
5 | "testing"
6 | )
7 |
8 | func TestConversionJSON(t *testing.T) {
9 |
10 | jsonString := `{"name":"Mat"}`
11 | o := MustFromJSON(jsonString)
12 |
13 | result, err := o.JSON()
14 |
15 | if assert.NoError(t, err) {
16 | assert.Equal(t, jsonString, result)
17 | }
18 |
19 | assert.Equal(t, jsonString, o.MustJSON())
20 |
21 | }
22 |
23 | func TestConversionJSONWithError(t *testing.T) {
24 |
25 | o := MSI()
26 | o["test"] = func() {}
27 |
28 | assert.Panics(t, func() {
29 | o.MustJSON()
30 | })
31 |
32 | _, err := o.JSON()
33 |
34 | assert.Error(t, err)
35 |
36 | }
37 |
38 | func TestConversionBase64(t *testing.T) {
39 |
40 | o := New(map[string]interface{}{"name": "Mat"})
41 |
42 | result, err := o.Base64()
43 |
44 | if assert.NoError(t, err) {
45 | assert.Equal(t, "eyJuYW1lIjoiTWF0In0=", result)
46 | }
47 |
48 | assert.Equal(t, "eyJuYW1lIjoiTWF0In0=", o.MustBase64())
49 |
50 | }
51 |
52 | func TestConversionBase64WithError(t *testing.T) {
53 |
54 | o := MSI()
55 | o["test"] = func() {}
56 |
57 | assert.Panics(t, func() {
58 | o.MustBase64()
59 | })
60 |
61 | _, err := o.Base64()
62 |
63 | assert.Error(t, err)
64 |
65 | }
66 |
67 | func TestConversionSignedBase64(t *testing.T) {
68 |
69 | o := New(map[string]interface{}{"name": "Mat"})
70 |
71 | result, err := o.SignedBase64("key")
72 |
73 | if assert.NoError(t, err) {
74 | assert.Equal(t, "eyJuYW1lIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6", result)
75 | }
76 |
77 | assert.Equal(t, "eyJuYW1lIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6", o.MustSignedBase64("key"))
78 |
79 | }
80 |
81 | func TestConversionSignedBase64WithError(t *testing.T) {
82 |
83 | o := MSI()
84 | o["test"] = func() {}
85 |
86 | assert.Panics(t, func() {
87 | o.MustSignedBase64("key")
88 | })
89 |
90 | _, err := o.SignedBase64("key")
91 |
92 | assert.Error(t, err)
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/doc.go:
--------------------------------------------------------------------------------
1 | // objx - Go package for dealing with maps, slices, JSON and other data.
2 | //
3 | // Overview
4 | //
5 | // Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes
6 | // a powerful `Get` method (among others) that allows you to easily and quickly get
7 | // access to data within the map, without having to worry too much about type assertions,
8 | // missing data, default values etc.
9 | //
10 | // Pattern
11 | //
12 | // Objx uses a preditable pattern to make access data from within `map[string]interface{}'s
13 | // easy.
14 | //
15 | // Call one of the `objx.` functions to create your `objx.Map` to get going:
16 | //
17 | // m, err := objx.FromJSON(json)
18 | //
19 | // NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong,
20 | // the rest will be optimistic and try to figure things out without panicking.
21 | //
22 | // Use `Get` to access the value you're interested in. You can use dot and array
23 | // notation too:
24 | //
25 | // m.Get("places[0].latlng")
26 | //
27 | // Once you have saught the `Value` you're interested in, you can use the `Is*` methods
28 | // to determine its type.
29 | //
30 | // if m.Get("code").IsStr() { /* ... */ }
31 | //
32 | // Or you can just assume the type, and use one of the strong type methods to
33 | // extract the real value:
34 | //
35 | // m.Get("code").Int()
36 | //
37 | // If there's no value there (or if it's the wrong type) then a default value
38 | // will be returned, or you can be explicit about the default value.
39 | //
40 | // Get("code").Int(-1)
41 | //
42 | // If you're dealing with a slice of data as a value, Objx provides many useful
43 | // methods for iterating, manipulating and selecting that data. You can find out more
44 | // by exploring the index below.
45 | //
46 | // Reading data
47 | //
48 | // A simple example of how to use Objx:
49 | //
50 | // // use MustFromJSON to make an objx.Map from some JSON
51 | // m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`)
52 | //
53 | // // get the details
54 | // name := m.Get("name").Str()
55 | // age := m.Get("age").Int()
56 | //
57 | // // get their nickname (or use their name if they
58 | // // don't have one)
59 | // nickname := m.Get("nickname").Str(name)
60 | //
61 | // Ranging
62 | //
63 | // Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For
64 | // example, to `range` the data, do what you would expect:
65 | //
66 | // m := objx.MustFromJSON(json)
67 | // for key, value := range m {
68 | //
69 | // /* ... do your magic ... */
70 | //
71 | // }
72 | package objx
73 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/fixture_test.go:
--------------------------------------------------------------------------------
1 | package objx
2 |
3 | import (
4 | "github.com/stretchr/testify/assert"
5 | "testing"
6 | )
7 |
8 | var fixtures = []struct {
9 | // name is the name of the fixture (used for reporting
10 | // failures)
11 | name string
12 | // data is the JSON data to be worked on
13 | data string
14 | // get is the argument(s) to pass to Get
15 | get interface{}
16 | // output is the expected output
17 | output interface{}
18 | }{
19 | {
20 | name: "Simple get",
21 | data: `{"name": "Mat"}`,
22 | get: "name",
23 | output: "Mat",
24 | },
25 | {
26 | name: "Get with dot notation",
27 | data: `{"address": {"city": "Boulder"}}`,
28 | get: "address.city",
29 | output: "Boulder",
30 | },
31 | {
32 | name: "Deep get with dot notation",
33 | data: `{"one": {"two": {"three": {"four": "hello"}}}}`,
34 | get: "one.two.three.four",
35 | output: "hello",
36 | },
37 | {
38 | name: "Get missing with dot notation",
39 | data: `{"one": {"two": {"three": {"four": "hello"}}}}`,
40 | get: "one.ten",
41 | output: nil,
42 | },
43 | {
44 | name: "Get with array notation",
45 | data: `{"tags": ["one", "two", "three"]}`,
46 | get: "tags[1]",
47 | output: "two",
48 | },
49 | {
50 | name: "Get with array and dot notation",
51 | data: `{"types": { "tags": ["one", "two", "three"]}}`,
52 | get: "types.tags[1]",
53 | output: "two",
54 | },
55 | {
56 | name: "Get with array and dot notation - field after array",
57 | data: `{"tags": [{"name":"one"}, {"name":"two"}, {"name":"three"}]}`,
58 | get: "tags[1].name",
59 | output: "two",
60 | },
61 | {
62 | name: "Complex get with array and dot notation",
63 | data: `{"tags": [{"list": [{"one":"pizza"}]}]}`,
64 | get: "tags[0].list[0].one",
65 | output: "pizza",
66 | },
67 | {
68 | name: "Get field from within string should be nil",
69 | data: `{"name":"Tyler"}`,
70 | get: "name.something",
71 | output: nil,
72 | },
73 | {
74 | name: "Get field from within string (using array accessor) should be nil",
75 | data: `{"numbers":["one", "two", "three"]}`,
76 | get: "numbers[0].nope",
77 | output: nil,
78 | },
79 | }
80 |
81 | func TestFixtures(t *testing.T) {
82 |
83 | for _, fixture := range fixtures {
84 |
85 | m := MustFromJSON(fixture.data)
86 |
87 | // get the value
88 | t.Logf("Running get fixture: \"%s\" (%v)", fixture.name, fixture)
89 | value := m.Get(fixture.get.(string))
90 |
91 | // make sure it matches
92 | assert.Equal(t, fixture.output, value.data,
93 | "Get fixture \"%s\" failed: %v", fixture.name, fixture,
94 | )
95 |
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/map_for_test.go:
--------------------------------------------------------------------------------
1 | package objx
2 |
3 | var TestMap map[string]interface{} = map[string]interface{}{
4 | "name": "Tyler",
5 | "address": map[string]interface{}{
6 | "city": "Salt Lake City",
7 | "state": "UT",
8 | },
9 | "numbers": []interface{}{"one", "two", "three", "four", "five"},
10 | }
11 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/map_test.go:
--------------------------------------------------------------------------------
1 | package objx
2 |
3 | import (
4 | "github.com/stretchr/testify/assert"
5 | "testing"
6 | )
7 |
8 | type Convertable struct {
9 | name string
10 | }
11 |
12 | func (c *Convertable) MSI() map[string]interface{} {
13 | return map[string]interface{}{"name": c.name}
14 | }
15 |
16 | type Unconvertable struct {
17 | name string
18 | }
19 |
20 | func TestMapCreation(t *testing.T) {
21 |
22 | o := New(nil)
23 | assert.Nil(t, o)
24 |
25 | o = New("Tyler")
26 | assert.Nil(t, o)
27 |
28 | unconvertable := &Unconvertable{name: "Tyler"}
29 | o = New(unconvertable)
30 | assert.Nil(t, o)
31 |
32 | convertable := &Convertable{name: "Tyler"}
33 | o = New(convertable)
34 | if assert.NotNil(t, convertable) {
35 | assert.Equal(t, "Tyler", o["name"], "Tyler")
36 | }
37 |
38 | o = MSI()
39 | if assert.NotNil(t, o) {
40 | assert.NotNil(t, o)
41 | }
42 |
43 | o = MSI("name", "Tyler")
44 | if assert.NotNil(t, o) {
45 | if assert.NotNil(t, o) {
46 | assert.Equal(t, o["name"], "Tyler")
47 | }
48 | }
49 |
50 | }
51 |
52 | func TestMapMustFromJSONWithError(t *testing.T) {
53 |
54 | _, err := FromJSON(`"name":"Mat"}`)
55 | assert.Error(t, err)
56 |
57 | }
58 |
59 | func TestMapFromJSON(t *testing.T) {
60 |
61 | o := MustFromJSON(`{"name":"Mat"}`)
62 |
63 | if assert.NotNil(t, o) {
64 | if assert.NotNil(t, o) {
65 | assert.Equal(t, "Mat", o["name"])
66 | }
67 | }
68 |
69 | }
70 |
71 | func TestMapFromJSONWithError(t *testing.T) {
72 |
73 | var m Map
74 |
75 | assert.Panics(t, func() {
76 | m = MustFromJSON(`"name":"Mat"}`)
77 | })
78 |
79 | assert.Nil(t, m)
80 |
81 | }
82 |
83 | func TestMapFromBase64String(t *testing.T) {
84 |
85 | base64String := "eyJuYW1lIjoiTWF0In0="
86 |
87 | o, err := FromBase64(base64String)
88 |
89 | if assert.NoError(t, err) {
90 | assert.Equal(t, o.Get("name").Str(), "Mat")
91 | }
92 |
93 | assert.Equal(t, MustFromBase64(base64String).Get("name").Str(), "Mat")
94 |
95 | }
96 |
97 | func TestMapFromBase64StringWithError(t *testing.T) {
98 |
99 | base64String := "eyJuYW1lIjoiTWFasd0In0="
100 |
101 | _, err := FromBase64(base64String)
102 |
103 | assert.Error(t, err)
104 |
105 | assert.Panics(t, func() {
106 | MustFromBase64(base64String)
107 | })
108 |
109 | }
110 |
111 | func TestMapFromSignedBase64String(t *testing.T) {
112 |
113 | base64String := "eyJuYW1lIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6"
114 |
115 | o, err := FromSignedBase64(base64String, "key")
116 |
117 | if assert.NoError(t, err) {
118 | assert.Equal(t, o.Get("name").Str(), "Mat")
119 | }
120 |
121 | assert.Equal(t, MustFromSignedBase64(base64String, "key").Get("name").Str(), "Mat")
122 |
123 | }
124 |
125 | func TestMapFromSignedBase64StringWithError(t *testing.T) {
126 |
127 | base64String := "eyJuYW1lasdIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6"
128 |
129 | _, err := FromSignedBase64(base64String, "key")
130 |
131 | assert.Error(t, err)
132 |
133 | assert.Panics(t, func() {
134 | MustFromSignedBase64(base64String, "key")
135 | })
136 |
137 | }
138 |
139 | func TestMapFromURLQuery(t *testing.T) {
140 |
141 | m, err := FromURLQuery("name=tyler&state=UT")
142 | if assert.NoError(t, err) && assert.NotNil(t, m) {
143 | assert.Equal(t, "tyler", m.Get("name").Str())
144 | assert.Equal(t, "UT", m.Get("state").Str())
145 | }
146 |
147 | }
148 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/mutations.go:
--------------------------------------------------------------------------------
1 | package objx
2 |
3 | // Exclude returns a new Map with the keys in the specified []string
4 | // excluded.
5 | func (d Map) Exclude(exclude []string) Map {
6 |
7 | excluded := make(Map)
8 | for k, v := range d {
9 | var shouldInclude bool = true
10 | for _, toExclude := range exclude {
11 | if k == toExclude {
12 | shouldInclude = false
13 | break
14 | }
15 | }
16 | if shouldInclude {
17 | excluded[k] = v
18 | }
19 | }
20 |
21 | return excluded
22 | }
23 |
24 | // Copy creates a shallow copy of the Obj.
25 | func (m Map) Copy() Map {
26 | copied := make(map[string]interface{})
27 | for k, v := range m {
28 | copied[k] = v
29 | }
30 | return New(copied)
31 | }
32 |
33 | // Merge blends the specified map with a copy of this map and returns the result.
34 | //
35 | // Keys that appear in both will be selected from the specified map.
36 | // This method requires that the wrapped object be a map[string]interface{}
37 | func (m Map) Merge(merge Map) Map {
38 | return m.Copy().MergeHere(merge)
39 | }
40 |
41 | // Merge blends the specified map with this map and returns the current map.
42 | //
43 | // Keys that appear in both will be selected from the specified map. The original map
44 | // will be modified. This method requires that
45 | // the wrapped object be a map[string]interface{}
46 | func (m Map) MergeHere(merge Map) Map {
47 |
48 | for k, v := range merge {
49 | m[k] = v
50 | }
51 |
52 | return m
53 |
54 | }
55 |
56 | // Transform builds a new Obj giving the transformer a chance
57 | // to change the keys and values as it goes. This method requires that
58 | // the wrapped object be a map[string]interface{}
59 | func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map {
60 | newMap := make(map[string]interface{})
61 | for k, v := range m {
62 | modifiedKey, modifiedVal := transformer(k, v)
63 | newMap[modifiedKey] = modifiedVal
64 | }
65 | return New(newMap)
66 | }
67 |
68 | // TransformKeys builds a new map using the specified key mapping.
69 | //
70 | // Unspecified keys will be unaltered.
71 | // This method requires that the wrapped object be a map[string]interface{}
72 | func (m Map) TransformKeys(mapping map[string]string) Map {
73 | return m.Transform(func(key string, value interface{}) (string, interface{}) {
74 |
75 | if newKey, ok := mapping[key]; ok {
76 | return newKey, value
77 | }
78 |
79 | return key, value
80 | })
81 | }
82 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/mutations_test.go:
--------------------------------------------------------------------------------
1 | package objx
2 |
3 | import (
4 | "github.com/stretchr/testify/assert"
5 | "testing"
6 | )
7 |
8 | func TestExclude(t *testing.T) {
9 |
10 | d := make(Map)
11 | d["name"] = "Mat"
12 | d["age"] = 29
13 | d["secret"] = "ABC"
14 |
15 | excluded := d.Exclude([]string{"secret"})
16 |
17 | assert.Equal(t, d["name"], excluded["name"])
18 | assert.Equal(t, d["age"], excluded["age"])
19 | assert.False(t, excluded.Has("secret"), "secret should be excluded")
20 |
21 | }
22 |
23 | func TestCopy(t *testing.T) {
24 |
25 | d1 := make(map[string]interface{})
26 | d1["name"] = "Tyler"
27 | d1["location"] = "UT"
28 |
29 | d1Obj := New(d1)
30 | d2Obj := d1Obj.Copy()
31 |
32 | d2Obj["name"] = "Mat"
33 |
34 | assert.Equal(t, d1Obj.Get("name").Str(), "Tyler")
35 | assert.Equal(t, d2Obj.Get("name").Str(), "Mat")
36 |
37 | }
38 |
39 | func TestMerge(t *testing.T) {
40 |
41 | d := make(map[string]interface{})
42 | d["name"] = "Mat"
43 |
44 | d1 := make(map[string]interface{})
45 | d1["name"] = "Tyler"
46 | d1["location"] = "UT"
47 |
48 | dObj := New(d)
49 | d1Obj := New(d1)
50 |
51 | merged := dObj.Merge(d1Obj)
52 |
53 | assert.Equal(t, merged.Get("name").Str(), d1Obj.Get("name").Str())
54 | assert.Equal(t, merged.Get("location").Str(), d1Obj.Get("location").Str())
55 | assert.Empty(t, dObj.Get("location").Str())
56 |
57 | }
58 |
59 | func TestMergeHere(t *testing.T) {
60 |
61 | d := make(map[string]interface{})
62 | d["name"] = "Mat"
63 |
64 | d1 := make(map[string]interface{})
65 | d1["name"] = "Tyler"
66 | d1["location"] = "UT"
67 |
68 | dObj := New(d)
69 | d1Obj := New(d1)
70 |
71 | merged := dObj.MergeHere(d1Obj)
72 |
73 | assert.Equal(t, dObj, merged, "With MergeHere, it should return the first modified map")
74 | assert.Equal(t, merged.Get("name").Str(), d1Obj.Get("name").Str())
75 | assert.Equal(t, merged.Get("location").Str(), d1Obj.Get("location").Str())
76 | assert.Equal(t, merged.Get("location").Str(), dObj.Get("location").Str())
77 | }
78 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/security.go:
--------------------------------------------------------------------------------
1 | package objx
2 |
3 | import (
4 | "crypto/sha1"
5 | "encoding/hex"
6 | )
7 |
8 | // HashWithKey hashes the specified string using the security
9 | // key.
10 | func HashWithKey(data, key string) string {
11 | hash := sha1.New()
12 | hash.Write([]byte(data + ":" + key))
13 | return hex.EncodeToString(hash.Sum(nil))
14 | }
15 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/security_test.go:
--------------------------------------------------------------------------------
1 | package objx
2 |
3 | import (
4 | "github.com/stretchr/testify/assert"
5 | "testing"
6 | )
7 |
8 | func TestHashWithKey(t *testing.T) {
9 |
10 | assert.Equal(t, "0ce84d8d01f2c7b6e0882b784429c54d280ea2d9", HashWithKey("abc", "def"))
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/simple_example_test.go:
--------------------------------------------------------------------------------
1 | package objx
2 |
3 | import (
4 | "github.com/stretchr/testify/assert"
5 | "testing"
6 | )
7 |
8 | func TestSimpleExample(t *testing.T) {
9 |
10 | // build a map from a JSON object
11 | o := MustFromJSON(`{"name":"Mat","foods":["indian","chinese"], "location":{"county":"hobbiton","city":"the shire"}}`)
12 |
13 | // Map can be used as a straight map[string]interface{}
14 | assert.Equal(t, o["name"], "Mat")
15 |
16 | // Get an Value object
17 | v := o.Get("name")
18 | assert.Equal(t, v, &Value{data: "Mat"})
19 |
20 | // Test the contained value
21 | assert.False(t, v.IsInt())
22 | assert.False(t, v.IsBool())
23 | assert.True(t, v.IsStr())
24 |
25 | // Get the contained value
26 | assert.Equal(t, v.Str(), "Mat")
27 |
28 | // Get a default value if the contained value is not of the expected type or does not exist
29 | assert.Equal(t, 1, v.Int(1))
30 |
31 | // Get a value by using array notation
32 | assert.Equal(t, "indian", o.Get("foods[0]").Data())
33 |
34 | // Set a value by using array notation
35 | o.Set("foods[0]", "italian")
36 | assert.Equal(t, "italian", o.Get("foods[0]").Str())
37 |
38 | // Get a value by using dot notation
39 | assert.Equal(t, "hobbiton", o.Get("location.county").Str())
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/tests.go:
--------------------------------------------------------------------------------
1 | package objx
2 |
3 | // Has gets whether there is something at the specified selector
4 | // or not.
5 | //
6 | // If m is nil, Has will always return false.
7 | func (m Map) Has(selector string) bool {
8 | if m == nil {
9 | return false
10 | }
11 | return !m.Get(selector).IsNil()
12 | }
13 |
14 | // IsNil gets whether the data is nil or not.
15 | func (v *Value) IsNil() bool {
16 | return v == nil || v.data == nil
17 | }
18 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/tests_test.go:
--------------------------------------------------------------------------------
1 | package objx
2 |
3 | import (
4 | "github.com/stretchr/testify/assert"
5 | "testing"
6 | )
7 |
8 | func TestHas(t *testing.T) {
9 |
10 | m := New(TestMap)
11 |
12 | assert.True(t, m.Has("name"))
13 | assert.True(t, m.Has("address.state"))
14 | assert.True(t, m.Has("numbers[4]"))
15 |
16 | assert.False(t, m.Has("address.state.nope"))
17 | assert.False(t, m.Has("address.nope"))
18 | assert.False(t, m.Has("nope"))
19 | assert.False(t, m.Has("numbers[5]"))
20 |
21 | m = nil
22 | assert.False(t, m.Has("nothing"))
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/value.go:
--------------------------------------------------------------------------------
1 | package objx
2 |
3 | // Value provides methods for extracting interface{} data in various
4 | // types.
5 | type Value struct {
6 | // data contains the raw data being managed by this Value
7 | data interface{}
8 | }
9 |
10 | // Data returns the raw data contained by this Value
11 | func (v *Value) Data() interface{} {
12 | return v.data
13 | }
14 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/objx/value_test.go:
--------------------------------------------------------------------------------
1 | package objx
2 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/testify/assert/errors.go:
--------------------------------------------------------------------------------
1 | package assert
2 |
3 | import (
4 | "errors"
5 | )
6 |
7 | // AnError is an error instance useful for testing. If the code does not care
8 | // about error specifics, and only needs to return the error for example, this
9 | // error should be used to make the test code more readable.
10 | var AnError = errors.New("assert.AnError general error for testing")
11 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/testify/assert/http_assertions_test.go:
--------------------------------------------------------------------------------
1 | package assert
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "net/url"
7 | "testing"
8 | )
9 |
10 | func httpOK(w http.ResponseWriter, r *http.Request) {
11 | w.WriteHeader(http.StatusOK)
12 | }
13 |
14 | func httpRedirect(w http.ResponseWriter, r *http.Request) {
15 | w.WriteHeader(http.StatusTemporaryRedirect)
16 | }
17 |
18 | func httpError(w http.ResponseWriter, r *http.Request) {
19 | w.WriteHeader(http.StatusInternalServerError)
20 | }
21 |
22 | func TestHTTPStatuses(t *testing.T) {
23 | assert := New(t)
24 | mockT := new(testing.T)
25 |
26 | assert.Equal(HTTPSuccess(mockT, httpOK, "GET", "/", nil), true)
27 | assert.Equal(HTTPSuccess(mockT, httpRedirect, "GET", "/", nil), false)
28 | assert.Equal(HTTPSuccess(mockT, httpError, "GET", "/", nil), false)
29 |
30 | assert.Equal(HTTPRedirect(mockT, httpOK, "GET", "/", nil), false)
31 | assert.Equal(HTTPRedirect(mockT, httpRedirect, "GET", "/", nil), true)
32 | assert.Equal(HTTPRedirect(mockT, httpError, "GET", "/", nil), false)
33 |
34 | assert.Equal(HTTPError(mockT, httpOK, "GET", "/", nil), false)
35 | assert.Equal(HTTPError(mockT, httpRedirect, "GET", "/", nil), false)
36 | assert.Equal(HTTPError(mockT, httpError, "GET", "/", nil), true)
37 | }
38 |
39 | func TestHTTPStatusesWrapper(t *testing.T) {
40 | assert := New(t)
41 | mockAssert := New(new(testing.T))
42 |
43 | assert.Equal(mockAssert.HTTPSuccess(httpOK, "GET", "/", nil), true)
44 | assert.Equal(mockAssert.HTTPSuccess(httpRedirect, "GET", "/", nil), false)
45 | assert.Equal(mockAssert.HTTPSuccess(httpError, "GET", "/", nil), false)
46 |
47 | assert.Equal(mockAssert.HTTPRedirect(httpOK, "GET", "/", nil), false)
48 | assert.Equal(mockAssert.HTTPRedirect(httpRedirect, "GET", "/", nil), true)
49 | assert.Equal(mockAssert.HTTPRedirect(httpError, "GET", "/", nil), false)
50 |
51 | assert.Equal(mockAssert.HTTPError(httpOK, "GET", "/", nil), false)
52 | assert.Equal(mockAssert.HTTPError(httpRedirect, "GET", "/", nil), false)
53 | assert.Equal(mockAssert.HTTPError(httpError, "GET", "/", nil), true)
54 | }
55 |
56 | func httpHelloName(w http.ResponseWriter, r *http.Request) {
57 | name := r.FormValue("name")
58 | w.Write([]byte(fmt.Sprintf("Hello, %s!", name)))
59 | }
60 |
61 | func TestHttpBody(t *testing.T) {
62 | assert := New(t)
63 | mockT := new(testing.T)
64 |
65 | assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
66 | assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
67 | assert.False(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
68 |
69 | assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
70 | assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
71 | assert.True(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
72 | }
73 |
74 | func TestHttpBodyWrappers(t *testing.T) {
75 | assert := New(t)
76 | mockAssert := New(new(testing.T))
77 |
78 | assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
79 | assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
80 | assert.False(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
81 |
82 | assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
83 | assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
84 | assert.True(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/stretchr/testify/mock/doc.go:
--------------------------------------------------------------------------------
1 | // Provides a system by which it is possible to mock your objects and verify calls are happening as expected.
2 | //
3 | // Example Usage
4 | //
5 | // The mock package provides an object, Mock, that tracks activity on another object. It is usually
6 | // embedded into a test object as shown below:
7 | //
8 | // type MyTestObject struct {
9 | // // add a Mock object instance
10 | // mock.Mock
11 | //
12 | // // other fields go here as normal
13 | // }
14 | //
15 | // When implementing the methods of an interface, you wire your functions up
16 | // to call the Mock.Called(args...) method, and return the appropriate values.
17 | //
18 | // For example, to mock a method that saves the name and age of a person and returns
19 | // the year of their birth or an error, you might write this:
20 | //
21 | // func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) {
22 | // args := o.Called(firstname, lastname, age)
23 | // return args.Int(0), args.Error(1)
24 | // }
25 | //
26 | // The Int, Error and Bool methods are examples of strongly typed getters that take the argument
27 | // index position. Given this argument list:
28 | //
29 | // (12, true, "Something")
30 | //
31 | // You could read them out strongly typed like this:
32 | //
33 | // args.Int(0)
34 | // args.Bool(1)
35 | // args.String(2)
36 | //
37 | // For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion:
38 | //
39 | // return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine)
40 | //
41 | // This may cause a panic if the object you are getting is nil (the type assertion will fail), in those
42 | // cases you should check for nil first.
43 | package mock
44 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 | install: go get -t -v ./...
3 | go: 1.2
4 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/COPYING:
--------------------------------------------------------------------------------
1 | Copyright (C) 2014 Alec Thomas
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/args.go:
--------------------------------------------------------------------------------
1 | package kingpin
2 |
3 | import "fmt"
4 |
5 | type argGroup struct {
6 | args []*ArgClause
7 | }
8 |
9 | func newArgGroup() *argGroup {
10 | return &argGroup{}
11 | }
12 |
13 | func (a *argGroup) have() bool {
14 | return len(a.args) > 0
15 | }
16 |
17 | func (a *argGroup) Arg(name, help string) *ArgClause {
18 | arg := newArg(name, help)
19 | a.args = append(a.args, arg)
20 | return arg
21 | }
22 |
23 | func (a *argGroup) init() error {
24 | required := 0
25 | seen := map[string]struct{}{}
26 | previousArgMustBeLast := false
27 | for i, arg := range a.args {
28 | if previousArgMustBeLast {
29 | return fmt.Errorf("Args() can't be followed by another argument '%s'", arg.name)
30 | }
31 | if arg.consumesRemainder() {
32 | previousArgMustBeLast = true
33 | }
34 | if _, ok := seen[arg.name]; ok {
35 | return fmt.Errorf("duplicate argument '%s'", arg.name)
36 | }
37 | seen[arg.name] = struct{}{}
38 | if arg.required && required != i {
39 | return fmt.Errorf("required arguments found after non-required")
40 | }
41 | if arg.required {
42 | required++
43 | }
44 | if err := arg.init(); err != nil {
45 | return err
46 | }
47 | }
48 | return nil
49 | }
50 |
51 | type ArgClause struct {
52 | parserMixin
53 | name string
54 | help string
55 | defaultValue string
56 | required bool
57 | action Action
58 | preAction Action
59 | }
60 |
61 | func newArg(name, help string) *ArgClause {
62 | a := &ArgClause{
63 | name: name,
64 | help: help,
65 | }
66 | return a
67 | }
68 |
69 | func (a *ArgClause) consumesRemainder() bool {
70 | if r, ok := a.value.(remainderArg); ok {
71 | return r.IsCumulative()
72 | }
73 | return false
74 | }
75 |
76 | // Required arguments must be input by the user. They can not have a Default() value provided.
77 | func (a *ArgClause) Required() *ArgClause {
78 | a.required = true
79 | return a
80 | }
81 |
82 | // Default value for this argument. It *must* be parseable by the value of the argument.
83 | func (a *ArgClause) Default(value string) *ArgClause {
84 | a.defaultValue = value
85 | return a
86 | }
87 |
88 | func (a *ArgClause) Action(action Action) *ArgClause {
89 | a.action = action
90 | return a
91 | }
92 |
93 | func (a *ArgClause) PreAction(action Action) *ArgClause {
94 | a.preAction = action
95 | return a
96 | }
97 |
98 | func (a *ArgClause) init() error {
99 | if a.required && a.defaultValue != "" {
100 | return fmt.Errorf("required argument '%s' with unusable default value", a.name)
101 | }
102 | if a.value == nil {
103 | return fmt.Errorf("no parser defined for arg '%s'", a.name)
104 | }
105 | return nil
106 | }
107 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/args_test.go:
--------------------------------------------------------------------------------
1 | package kingpin
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | )
8 |
9 | func TestArgRemainder(t *testing.T) {
10 | app := New("test", "")
11 | v := app.Arg("test", "").Strings()
12 | args := []string{"hello", "world"}
13 | _, err := app.Parse(args)
14 | assert.NoError(t, err)
15 | assert.Equal(t, args, *v)
16 | }
17 |
18 | func TestArgRemainderErrorsWhenNotLast(t *testing.T) {
19 | a := newArgGroup()
20 | a.Arg("test", "").Strings()
21 | a.Arg("test2", "").String()
22 | assert.Error(t, a.init())
23 | }
24 |
25 | func TestArgMultipleRequired(t *testing.T) {
26 | terminated := false
27 | app := New("test", "")
28 | app.Version("0.0.0")
29 | app.Arg("a", "").Required().String()
30 | app.Arg("b", "").Required().String()
31 | app.Terminate(func(int) { terminated = true })
32 |
33 | _, err := app.Parse([]string{})
34 | assert.Error(t, err)
35 | _, err = app.Parse([]string{"A"})
36 | assert.Error(t, err)
37 | _, err = app.Parse([]string{"A", "B"})
38 | assert.NoError(t, err)
39 | _, err = app.Parse([]string{"--version"})
40 | assert.True(t, terminated)
41 | }
42 |
43 | func TestInvalidArgsDefaultCanBeOverridden(t *testing.T) {
44 | app := New("test", "")
45 | app.Arg("a", "").Default("invalid").Bool()
46 | _, err := app.Parse([]string{})
47 | assert.Error(t, err)
48 | }
49 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/cmd.go:
--------------------------------------------------------------------------------
1 | package kingpin
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | type cmdGroup struct {
9 | app *Application
10 | parent *CmdClause
11 | commands map[string]*CmdClause
12 | commandOrder []*CmdClause
13 | }
14 |
15 | func newCmdGroup(app *Application) *cmdGroup {
16 | return &cmdGroup{
17 | app: app,
18 | commands: make(map[string]*CmdClause),
19 | }
20 | }
21 |
22 | func (c *cmdGroup) flattenedCommands() (out []*CmdClause) {
23 | for _, cmd := range c.commandOrder {
24 | if len(cmd.commands) == 0 {
25 | out = append(out, cmd)
26 | }
27 | out = append(out, cmd.flattenedCommands()...)
28 | }
29 | return
30 | }
31 |
32 | func (c *cmdGroup) addCommand(name, help string) *CmdClause {
33 | cmd := newCommand(c.app, name, help)
34 | c.commands[name] = cmd
35 | c.commandOrder = append(c.commandOrder, cmd)
36 | return cmd
37 | }
38 |
39 | func (c *cmdGroup) init() error {
40 | seen := map[string]bool{}
41 | for _, cmd := range c.commandOrder {
42 | if seen[cmd.name] {
43 | return fmt.Errorf("duplicate command '%s'", cmd.name)
44 | }
45 | seen[cmd.name] = true
46 | if err := cmd.init(); err != nil {
47 | return err
48 | }
49 | }
50 | return nil
51 | }
52 | func (c *cmdGroup) have() bool {
53 | return len(c.commands) > 0
54 | }
55 |
56 | type CmdClauseValidator func(*CmdClause) error
57 |
58 | // A CmdClause is a single top-level command. It encapsulates a set of flags
59 | // and either subcommands or positional arguments.
60 | type CmdClause struct {
61 | *flagGroup
62 | *argGroup
63 | *cmdGroup
64 | app *Application
65 | name string
66 | help string
67 | action Action
68 | preAction Action
69 | validator CmdClauseValidator
70 | hidden bool
71 | }
72 |
73 | func newCommand(app *Application, name, help string) *CmdClause {
74 | c := &CmdClause{
75 | flagGroup: newFlagGroup(),
76 | argGroup: newArgGroup(),
77 | cmdGroup: newCmdGroup(app),
78 | app: app,
79 | name: name,
80 | help: help,
81 | }
82 | return c
83 | }
84 |
85 | // Validate sets a validation function to run when parsing.
86 | func (c *CmdClause) Validate(validator CmdClauseValidator) *CmdClause {
87 | c.validator = validator
88 | return c
89 | }
90 |
91 | func (c *CmdClause) FullCommand() string {
92 | out := []string{c.name}
93 | for p := c.parent; p != nil; p = p.parent {
94 | out = append([]string{p.name}, out...)
95 | }
96 | return strings.Join(out, " ")
97 | }
98 |
99 | // Command adds a new sub-command.
100 | func (c *CmdClause) Command(name, help string) *CmdClause {
101 | cmd := c.addCommand(name, help)
102 | cmd.parent = c
103 | return cmd
104 | }
105 |
106 | func (c *CmdClause) Action(action Action) *CmdClause {
107 | c.action = action
108 | return c
109 | }
110 |
111 | func (c *CmdClause) PreAction(action Action) *CmdClause {
112 | c.preAction = action
113 | return c
114 | }
115 |
116 | func (c *CmdClause) init() error {
117 | if err := c.flagGroup.init(); err != nil {
118 | return err
119 | }
120 | if c.argGroup.have() && c.cmdGroup.have() {
121 | return fmt.Errorf("can't mix Arg()s with Command()s")
122 | }
123 | if err := c.argGroup.init(); err != nil {
124 | return err
125 | }
126 | if err := c.cmdGroup.init(); err != nil {
127 | return err
128 | }
129 | return nil
130 | }
131 |
132 | func (c *CmdClause) Hidden() *CmdClause {
133 | c.hidden = true
134 | return c
135 | }
136 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/cmd_test.go:
--------------------------------------------------------------------------------
1 | package kingpin
2 |
3 | import (
4 | "strings"
5 |
6 | "github.com/stretchr/testify/assert"
7 |
8 | "testing"
9 | )
10 |
11 | func parseAndExecute(app *Application, context *ParseContext) (string, error) {
12 | if _, err := parse(context, app); err != nil {
13 | return "", err
14 | }
15 | return app.execute(context)
16 | }
17 |
18 | func TestNestedCommands(t *testing.T) {
19 | app := New("app", "")
20 | sub1 := app.Command("sub1", "")
21 | sub1.Flag("sub1", "")
22 | subsub1 := sub1.Command("sub1sub1", "")
23 | subsub1.Command("sub1sub1end", "")
24 |
25 | sub2 := app.Command("sub2", "")
26 | sub2.Flag("sub2", "")
27 | sub2.Command("sub2sub1", "")
28 |
29 | context := tokenize([]string{"sub1", "sub1sub1", "sub1sub1end"})
30 | selected, err := parseAndExecute(app, context)
31 | assert.NoError(t, err)
32 | assert.True(t, context.EOL())
33 | assert.Equal(t, "sub1 sub1sub1 sub1sub1end", selected)
34 | }
35 |
36 | func TestNestedCommandsWithArgs(t *testing.T) {
37 | app := New("app", "")
38 | cmd := app.Command("a", "").Command("b", "")
39 | a := cmd.Arg("a", "").String()
40 | b := cmd.Arg("b", "").String()
41 | context := tokenize([]string{"a", "b", "c", "d"})
42 | selected, err := parseAndExecute(app, context)
43 | assert.NoError(t, err)
44 | assert.True(t, context.EOL())
45 | assert.Equal(t, "a b", selected)
46 | assert.Equal(t, "c", *a)
47 | assert.Equal(t, "d", *b)
48 | }
49 |
50 | func TestNestedCommandsWithFlags(t *testing.T) {
51 | app := New("app", "")
52 | cmd := app.Command("a", "").Command("b", "")
53 | a := cmd.Flag("aaa", "").Short('a').String()
54 | b := cmd.Flag("bbb", "").Short('b').String()
55 | err := app.init()
56 | assert.NoError(t, err)
57 | context := tokenize(strings.Split("a b --aaa x -b x", " "))
58 | selected, err := parseAndExecute(app, context)
59 | assert.NoError(t, err)
60 | assert.True(t, context.EOL())
61 | assert.Equal(t, "a b", selected)
62 | assert.Equal(t, "x", *a)
63 | assert.Equal(t, "x", *b)
64 | }
65 |
66 | func TestNestedCommandWithMergedFlags(t *testing.T) {
67 | app := New("app", "")
68 | cmd0 := app.Command("a", "")
69 | cmd0f0 := cmd0.Flag("aflag", "").Bool()
70 | // cmd1 := app.Command("b", "")
71 | // cmd1f0 := cmd0.Flag("bflag", "").Bool()
72 | cmd00 := cmd0.Command("aa", "")
73 | cmd00f0 := cmd00.Flag("aaflag", "").Bool()
74 | err := app.init()
75 | assert.NoError(t, err)
76 | context := tokenize(strings.Split("a aa --aflag --aaflag", " "))
77 | selected, err := parseAndExecute(app, context)
78 | assert.NoError(t, err)
79 | assert.True(t, *cmd0f0)
80 | assert.True(t, *cmd00f0)
81 | assert.Equal(t, "a aa", selected)
82 | }
83 |
84 | func TestNestedCommandWithDuplicateFlagErrors(t *testing.T) {
85 | app := New("app", "")
86 | app.Flag("test", "").Bool()
87 | app.Command("cmd0", "").Flag("test", "").Bool()
88 | err := app.init()
89 | assert.Error(t, err)
90 | }
91 |
92 | func TestNestedCommandWithArgAndMergedFlags(t *testing.T) {
93 | app := New("app", "")
94 | cmd0 := app.Command("a", "")
95 | cmd0f0 := cmd0.Flag("aflag", "").Bool()
96 | // cmd1 := app.Command("b", "")
97 | // cmd1f0 := cmd0.Flag("bflag", "").Bool()
98 | cmd00 := cmd0.Command("aa", "")
99 | cmd00a0 := cmd00.Arg("arg", "").String()
100 | cmd00f0 := cmd00.Flag("aaflag", "").Bool()
101 | err := app.init()
102 | assert.NoError(t, err)
103 | context := tokenize(strings.Split("a aa hello --aflag --aaflag", " "))
104 | selected, err := parseAndExecute(app, context)
105 | assert.NoError(t, err)
106 | assert.True(t, *cmd0f0)
107 | assert.True(t, *cmd00f0)
108 | assert.Equal(t, "a aa", selected)
109 | assert.Equal(t, "hello", *cmd00a0)
110 | }
111 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/doc.go:
--------------------------------------------------------------------------------
1 | // Package kingpin provides command line interfaces like this:
2 | //
3 | // $ chat
4 | // usage: chat [] [] [ ...]
5 | //
6 | // Flags:
7 | // --debug enable debug mode
8 | // --help Show help.
9 | // --server=127.0.0.1 server address
10 | //
11 | // Commands:
12 | // help
13 | // Show help for a command.
14 | //
15 | // post []
16 | // Post a message to a channel.
17 | //
18 | // register
19 | // Register a new user.
20 | //
21 | // $ chat help post
22 | // usage: chat [] post [] []
23 | //
24 | // Post a message to a channel.
25 | //
26 | // Flags:
27 | // --image=IMAGE image to post
28 | //
29 | // Args:
30 | // channel to post to
31 | // [] text to post
32 | // $ chat post --image=~/Downloads/owls.jpg pics
33 | //
34 | // From code like this:
35 | //
36 | // package main
37 | //
38 | // import "gopkg.in/alecthomas/kingpin.v1"
39 | //
40 | // var (
41 | // debug = kingpin.Flag("debug", "enable debug mode").Default("false").Bool()
42 | // serverIP = kingpin.Flag("server", "server address").Default("127.0.0.1").IP()
43 | //
44 | // register = kingpin.Command("register", "Register a new user.")
45 | // registerNick = register.Arg("nick", "nickname for user").Required().String()
46 | // registerName = register.Arg("name", "name of user").Required().String()
47 | //
48 | // post = kingpin.Command("post", "Post a message to a channel.")
49 | // postImage = post.Flag("image", "image to post").ExistingFile()
50 | // postChannel = post.Arg("channel", "channel to post to").Required().String()
51 | // postText = post.Arg("text", "text to post").String()
52 | // )
53 | //
54 | // func main() {
55 | // switch kingpin.Parse() {
56 | // // Register user
57 | // case "register":
58 | // println(*registerNick)
59 | //
60 | // // Post message
61 | // case "post":
62 | // if *postImage != nil {
63 | // }
64 | // if *postText != "" {
65 | // }
66 | // }
67 | // }
68 | package kingpin
69 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples/chat1/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/alecthomas/kingpin"
7 | )
8 |
9 | var (
10 | debug = kingpin.Flag("debug", "Enable debug mode.").Bool()
11 | timeout = kingpin.Flag("timeout", "Timeout waiting for ping.").Default("5s").OverrideDefaultFromEnvar("PING_TIMEOUT").Short('t').Duration()
12 | ip = kingpin.Arg("ip", "IP address to ping.").Required().IP()
13 | count = kingpin.Arg("count", "Number of packets to send").Int()
14 | )
15 |
16 | func main() {
17 | kingpin.Version("0.0.1")
18 | kingpin.Parse()
19 | fmt.Printf("Would ping: %s with timeout %s and count %d", *ip, *timeout, *count)
20 | }
21 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples/chat2/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 | "strings"
6 |
7 | "github.com/alecthomas/kingpin"
8 | )
9 |
10 | var (
11 | app = kingpin.New("chat", "A command-line chat application.")
12 | debug = app.Flag("debug", "Enable debug mode.").Bool()
13 | serverIP = app.Flag("server", "Server address.").Default("127.0.0.1").IP()
14 |
15 | register = app.Command("register", "Register a new user.")
16 | registerNick = register.Arg("nick", "Nickname for user.").Required().String()
17 | registerName = register.Arg("name", "Name of user.").Required().String()
18 |
19 | post = app.Command("post", "Post a message to a channel.")
20 | postImage = post.Flag("image", "Image to post.").File()
21 | postChannel = post.Arg("channel", "Channel to post to.").Required().String()
22 | postText = post.Arg("text", "Text to post.").Strings()
23 | )
24 |
25 | func main() {
26 | switch kingpin.MustParse(app.Parse(os.Args[1:])) {
27 | // Register user
28 | case register.FullCommand():
29 | println(*registerNick)
30 |
31 | // Post message
32 | case post.FullCommand():
33 | if *postImage != nil {
34 | }
35 | text := strings.Join(*postText, " ")
36 | println("Post:", text)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples/curl/main.go:
--------------------------------------------------------------------------------
1 | // A curl-like HTTP command-line client.
2 | package main
3 |
4 | import (
5 | "errors"
6 | "fmt"
7 | "io"
8 | "net/http"
9 | "os"
10 | "strings"
11 |
12 | "github.com/alecthomas/kingpin"
13 | )
14 |
15 | var (
16 | timeout = kingpin.Flag("timeout", "Set connection timeout.").Short('t').Default("5s").Duration()
17 | headers = HTTPHeader(kingpin.Flag("headers", "Add HTTP headers to the request.").Short('H').PlaceHolder("HEADER=VALUE"))
18 |
19 | get = kingpin.Command("get", "GET a resource.")
20 | getFlag = get.Flag("test", "Test flag").Bool()
21 | getURL = get.Command("url", "Retrieve a URL.")
22 | getURLURL = getURL.Arg("url", "URL to GET.").Required().URL()
23 | getFile = get.Command("file", "Retrieve a file.")
24 | getFileFile = getFile.Arg("file", "File to retrieve.").Required().ExistingFile()
25 |
26 | post = kingpin.Command("post", "POST a resource.")
27 | postData = post.Flag("data", "Key-value data to POST").Short('d').PlaceHolder("KEY:VALUE").StringMap()
28 | postBinaryFile = post.Flag("data-binary", "File with binary data to POST.").File()
29 | postURL = post.Arg("url", "URL to POST to.").Required().URL()
30 | )
31 |
32 | type HTTPHeaderValue http.Header
33 |
34 | func (h HTTPHeaderValue) Set(value string) error {
35 | parts := strings.SplitN(value, "=", 2)
36 | if len(parts) != 2 {
37 | return fmt.Errorf("expected HEADER=VALUE got '%s'", value)
38 | }
39 | (http.Header)(h).Add(parts[0], parts[1])
40 | return nil
41 | }
42 |
43 | func (h HTTPHeaderValue) String() string {
44 | return ""
45 | }
46 |
47 | func HTTPHeader(s kingpin.Settings) (target *http.Header) {
48 | target = &http.Header{}
49 | s.SetValue((*HTTPHeaderValue)(target))
50 | return
51 | }
52 |
53 | func applyRequest(req *http.Request) error {
54 | req.Header = *headers
55 | resp, err := http.DefaultClient.Do(req)
56 | if err != nil {
57 | return err
58 | }
59 | defer resp.Body.Close()
60 | if resp.StatusCode < 200 || resp.StatusCode > 299 {
61 | return fmt.Errorf("HTTP request failed: %s", resp.Status)
62 | }
63 | _, err = io.Copy(os.Stdout, resp.Body)
64 | return err
65 | }
66 |
67 | func apply(method string, url string) error {
68 | req, err := http.NewRequest(method, url, nil)
69 | if err != nil {
70 | return err
71 | }
72 | return applyRequest(req)
73 | }
74 |
75 | func applyPOST() error {
76 | req, err := http.NewRequest("POST", (*postURL).String(), nil)
77 | if err != nil {
78 | return err
79 | }
80 | if len(*postData) > 0 {
81 | for key, value := range *postData {
82 | req.Form.Set(key, value)
83 | }
84 | } else if postBinaryFile != nil {
85 | if headers.Get("Content-Type") != "" {
86 | headers.Set("Content-Type", "application/octet-stream")
87 | }
88 | req.Body = *postBinaryFile
89 | } else {
90 | return errors.New("--data or --data-binary must be provided to POST")
91 | }
92 | return applyRequest(req)
93 | }
94 |
95 | func main() {
96 | kingpin.UsageTemplate(kingpin.CompactUsageTemplate).Version("1.0").Author("Alec Thomas")
97 | kingpin.CommandLine.Help = "An example implementation of curl."
98 | switch kingpin.Parse() {
99 | case "get url":
100 | kingpin.FatalIfError(apply("GET", (*getURLURL).String()), "GET failed")
101 |
102 | case "post":
103 | kingpin.FatalIfError(applyPOST(), "POST failed")
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples/modular/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 |
7 | "github.com/alecthomas/kingpin"
8 | )
9 |
10 | // Context for "ls" command
11 | type LsCommand struct {
12 | All bool
13 | }
14 |
15 | func (l *LsCommand) run(c *kingpin.ParseContext) error {
16 | fmt.Printf("all=%v\n", l.All)
17 | return nil
18 | }
19 |
20 | func configureLsCommand(app *kingpin.Application) {
21 | c := &LsCommand{}
22 | ls := app.Command("ls", "List files.").Action(c.run)
23 | ls.Flag("all", "List all files.").Short('a').BoolVar(&c.All)
24 | }
25 |
26 | func main() {
27 | app := kingpin.New("modular", "My modular application.")
28 | configureLsCommand(app)
29 | kingpin.MustParse(app.Parse(os.Args[1:]))
30 | }
31 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples/ping/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/alecthomas/kingpin"
7 | )
8 |
9 | var (
10 | debug = kingpin.Flag("debug", "Enable debug mode.").Bool()
11 | timeout = kingpin.Flag("timeout", "Timeout waiting for ping.").OverrideDefaultFromEnvar("PING_TIMEOUT").Required().Short('t').Duration()
12 | ip = kingpin.Arg("ip", "IP address to ping.").Required().IP()
13 | count = kingpin.Arg("count", "Number of packets to send").Int()
14 | )
15 |
16 | func main() {
17 | kingpin.Version("0.0.1")
18 | kingpin.Parse()
19 | fmt.Printf("Would ping: %s with timeout %s and count %d", *ip, *timeout, *count)
20 | }
21 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples_test.go:
--------------------------------------------------------------------------------
1 | package kingpin
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "strings"
7 | )
8 |
9 | type HTTPHeaderValue http.Header
10 |
11 | func (h *HTTPHeaderValue) Set(value string) error {
12 | parts := strings.SplitN(value, ":", 2)
13 | if len(parts) != 2 {
14 | return fmt.Errorf("expected HEADER:VALUE got '%s'", value)
15 | }
16 | (*http.Header)(h).Add(parts[0], parts[1])
17 | return nil
18 | }
19 |
20 | func (h *HTTPHeaderValue) String() string {
21 | return ""
22 | }
23 |
24 | func HTTPHeader(s Settings) (target *http.Header) {
25 | target = new(http.Header)
26 | s.SetValue((*HTTPHeaderValue)(target))
27 | return
28 | }
29 |
30 | // This example ilustrates how to define custom parsers. HTTPHeader
31 | // cumulatively parses each encountered --header flag into a http.Header struct.
32 | func ExampleValue() {
33 | var (
34 | curl = New("curl", "transfer a URL")
35 | headers = HTTPHeader(curl.Flag("headers", "Add HTTP headers to the request.").Short('H').PlaceHolder("HEADER:VALUE"))
36 | )
37 |
38 | curl.Parse([]string{"-H Content-Type:application/octet-stream"})
39 | for key, value := range *headers {
40 | fmt.Printf("%s = %s\n", key, value)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/flags_test.go:
--------------------------------------------------------------------------------
1 | package kingpin
2 |
3 | import (
4 | "os"
5 |
6 | "github.com/stretchr/testify/assert"
7 |
8 | "testing"
9 | )
10 |
11 | func TestBool(t *testing.T) {
12 | app := New("test", "")
13 | b := app.Flag("b", "").Bool()
14 | _, err := app.Parse([]string{"--b"})
15 | assert.NoError(t, err)
16 | assert.True(t, *b)
17 | }
18 |
19 | func TestNoBool(t *testing.T) {
20 | fg := newFlagGroup()
21 | f := fg.Flag("b", "").Default("true")
22 | b := f.Bool()
23 | fg.init()
24 | tokens := tokenize([]string{"--no-b"})
25 | err := fg.parse(tokens)
26 | assert.NoError(t, err)
27 | assert.False(t, *b)
28 | }
29 |
30 | func TestNegateNonBool(t *testing.T) {
31 | fg := newFlagGroup()
32 | f := fg.Flag("b", "")
33 | f.Int()
34 | fg.init()
35 | tokens := tokenize([]string{"--no-b"})
36 | err := fg.parse(tokens)
37 | assert.Error(t, err)
38 | }
39 |
40 | func TestInvalidFlagDefaultCanBeOverridden(t *testing.T) {
41 | app := New("test", "")
42 | app.Flag("a", "").Default("invalid").Bool()
43 | _, err := app.Parse([]string{})
44 | assert.Error(t, err)
45 | }
46 |
47 | func TestRequiredFlag(t *testing.T) {
48 | app := New("test", "")
49 | app.Version("0.0.0")
50 | app.Terminate(nil)
51 | app.Flag("a", "").Required().Bool()
52 | _, err := app.Parse([]string{"--a"})
53 | assert.NoError(t, err)
54 | _, err = app.Parse([]string{})
55 | assert.Error(t, err)
56 | _, err = app.Parse([]string{"--version"})
57 | }
58 |
59 | func TestShortFlag(t *testing.T) {
60 | app := New("test", "")
61 | f := app.Flag("long", "").Short('s').Bool()
62 | _, err := app.Parse([]string{"-s"})
63 | assert.NoError(t, err)
64 | assert.True(t, *f)
65 | }
66 |
67 | func TestCombinedShortFlags(t *testing.T) {
68 | app := New("test", "")
69 | a := app.Flag("short0", "").Short('0').Bool()
70 | b := app.Flag("short1", "").Short('1').Bool()
71 | c := app.Flag("short2", "").Short('2').Bool()
72 | _, err := app.Parse([]string{"-01"})
73 | assert.NoError(t, err)
74 | assert.True(t, *a)
75 | assert.True(t, *b)
76 | assert.False(t, *c)
77 | }
78 |
79 | func TestCombinedShortFlagArg(t *testing.T) {
80 | a := New("test", "")
81 | n := a.Flag("short", "").Short('s').Int()
82 | _, err := a.Parse([]string{"-s10"})
83 | assert.NoError(t, err)
84 | assert.Equal(t, 10, *n)
85 | }
86 |
87 | func TestEmptyShortFlagIsAnError(t *testing.T) {
88 | _, err := New("test", "").Parse([]string{"-"})
89 | assert.Error(t, err)
90 | }
91 |
92 | func TestRequiredWithEnvarMissingErrors(t *testing.T) {
93 | app := New("test", "")
94 | app.Flag("t", "").OverrideDefaultFromEnvar("TEST_ENVAR").Required().Int()
95 | _, err := app.Parse([]string{})
96 | assert.Error(t, err)
97 | }
98 |
99 | func TestRequiredWithEnvar(t *testing.T) {
100 | os.Setenv("TEST_ENVAR", "123")
101 | app := New("test", "")
102 | flag := app.Flag("t", "").OverrideDefaultFromEnvar("TEST_ENVAR").Required().Int()
103 | _, err := app.Parse([]string{})
104 | assert.NoError(t, err)
105 | assert.Equal(t, 123, *flag)
106 | }
107 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/genrepeated/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "os/exec"
6 | "strings"
7 | "text/template"
8 |
9 | "os"
10 | )
11 |
12 | const (
13 | tmpl = `package kingpin
14 |
15 | // This file is autogenerated by "go generate .". Do not modify.
16 |
17 | {{range .}}
18 | // {{if .Comment}}{{.Comment}}{{else}}{{.|Name}} accumulates {{.Type}} values into a slice.{{end}}
19 | func (p *parserMixin) {{.|Name}}() (target *[]{{.Type}}) {
20 | target = new([]{{.Type}})
21 | p.{{.|Name}}Var(target)
22 | return
23 | }
24 |
25 | func (p *parserMixin) {{.|Name}}Var(target *[]{{.Type}}) {
26 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return new{{.Name}}Value(v.(*{{.Type}})) }))
27 | }
28 |
29 | {{end}}
30 | `
31 | )
32 |
33 | type Repeated struct {
34 | Name string `json:"name"`
35 | Comment string `json:"comment"`
36 | Plural string `json:"plural"`
37 | Type string `json:"type"`
38 | }
39 |
40 | func fatalIfError(err error) {
41 | if err != nil {
42 | panic(err)
43 | }
44 | }
45 |
46 | func main() {
47 | r, err := os.Open("repeated.json")
48 | fatalIfError(err)
49 | defer r.Close()
50 |
51 | v := []Repeated{}
52 | err = json.NewDecoder(r).Decode(&v)
53 | fatalIfError(err)
54 |
55 | t, err := template.New("genrepeated").Funcs(template.FuncMap{
56 | "Lower": strings.ToLower,
57 | "Name": func(v *Repeated) string {
58 | if v.Plural != "" {
59 | return v.Plural
60 | }
61 | return v.Name + "List"
62 | },
63 | }).Parse(tmpl)
64 | fatalIfError(err)
65 |
66 | w, err := os.Create("repeated.go")
67 | fatalIfError(err)
68 | defer w.Close()
69 |
70 | err = t.Execute(w, v)
71 | fatalIfError(err)
72 |
73 | err = exec.Command("goimports", "-w", "repeated.go").Run()
74 | fatalIfError(err)
75 | }
76 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/global.go:
--------------------------------------------------------------------------------
1 | package kingpin
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 | )
7 |
8 | var (
9 | // CommandLine is the default Kingpin parser.
10 | CommandLine = New(filepath.Base(os.Args[0]), "")
11 | )
12 |
13 | // Command adds a new command to the default parser.
14 | func Command(name, help string) *CmdClause {
15 | return CommandLine.Command(name, help)
16 | }
17 |
18 | // Flag adds a new flag to the default parser.
19 | func Flag(name, help string) *FlagClause {
20 | return CommandLine.Flag(name, help)
21 | }
22 |
23 | // Arg adds a new argument to the top-level of the default parser.
24 | func Arg(name, help string) *ArgClause {
25 | return CommandLine.Arg(name, help)
26 | }
27 |
28 | // Parse and return the selected command. Will call the termination handler if
29 | // an error is encountered.
30 | func Parse() string {
31 | selected := MustParse(CommandLine.Parse(os.Args[1:]))
32 | if selected == "" && CommandLine.cmdGroup.have() {
33 | Usage()
34 | CommandLine.terminate(0)
35 | }
36 | return selected
37 | }
38 |
39 | // Errorf prints an error message to stderr.
40 | func Errorf(format string, args ...interface{}) {
41 | CommandLine.Errorf(format, args...)
42 | }
43 |
44 | // Fatalf prints an error message to stderr and exits.
45 | func Fatalf(format string, args ...interface{}) {
46 | CommandLine.Fatalf(format, args...)
47 | }
48 |
49 | // FatalIfError prints an error and exits if err is not nil. The error is printed
50 | // with the given prefix.
51 | func FatalIfError(err error, format string, args ...interface{}) {
52 | CommandLine.FatalIfError(err, format, args...)
53 | }
54 |
55 | // FatalUsage prints an error message followed by usage information, then
56 | // exits with a non-zero status.
57 | func FatalUsage(format string, args ...interface{}) {
58 | CommandLine.FatalUsage(format, args...)
59 | }
60 |
61 | // FatalUsageContext writes a printf formatted error message to stderr, then
62 | // usage information for the given ParseContext, before exiting.
63 | func FatalUsageContext(context *ParseContext, format string, args ...interface{}) {
64 | CommandLine.FatalUsageContext(context, format, args...)
65 | }
66 |
67 | // Usage prints usage to stderr.
68 | func Usage() {
69 | CommandLine.Usage(os.Args[1:])
70 | }
71 |
72 | // Set global usage template to use (defaults to DefaultUsageTemplate).
73 | func UsageTemplate(template string) *Application {
74 | return CommandLine.UsageTemplate(template)
75 | }
76 |
77 | // MustParse can be used with app.Parse(args) to exit with an error if parsing fails.
78 | func MustParse(command string, err error) string {
79 | if err != nil {
80 | Fatalf("%s, try --help", err)
81 | }
82 | return command
83 | }
84 |
85 | // Version adds a flag for displaying the application version number.
86 | func Version(version string) *Application {
87 | return CommandLine.Version(version)
88 | }
89 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/guesswidth.go:
--------------------------------------------------------------------------------
1 | // +build !linux,!freebsd,!darwin,!dragonfly,!netbsd,!openbsd
2 |
3 | package kingpin
4 |
5 | import "io"
6 |
7 | func guessWidth(w io.Writer) int {
8 | return 80
9 | }
10 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/guesswidth_unix.go:
--------------------------------------------------------------------------------
1 | // +build linux freebsd darwin dragonfly netbsd openbsd
2 |
3 | package kingpin
4 |
5 | import (
6 | "io"
7 | "os"
8 | "strconv"
9 | "syscall"
10 | "unsafe"
11 | )
12 |
13 | func guessWidth(w io.Writer) int {
14 | // check if COLUMNS env is set to comply with
15 | // http://pubs.opengroup.org/onlinepubs/009604499/basedefs/xbd_chap08.html
16 | colsStr := os.Getenv("COLUMNS")
17 | if colsStr != "" {
18 | if cols, err := strconv.Atoi(colsStr); err == nil {
19 | return cols
20 | }
21 | }
22 |
23 | if t, ok := w.(*os.File); ok {
24 | fd := t.Fd()
25 | var dimensions [4]uint16
26 |
27 | if _, _, err := syscall.Syscall6(
28 | syscall.SYS_IOCTL,
29 | uintptr(fd),
30 | uintptr(syscall.TIOCGWINSZ),
31 | uintptr(unsafe.Pointer(&dimensions)),
32 | 0, 0, 0,
33 | ); err == 0 {
34 | return int(dimensions[1])
35 | }
36 | }
37 | return 80
38 | }
39 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/parser_test.go:
--------------------------------------------------------------------------------
1 | package kingpin
2 |
3 | import (
4 | "io/ioutil"
5 | "os"
6 | "testing"
7 |
8 | "github.com/stretchr/testify/assert"
9 | )
10 |
11 | func TestParserExpandFromFile(t *testing.T) {
12 | f, err := ioutil.TempFile("", "")
13 | assert.NoError(t, err)
14 | defer os.Remove(f.Name())
15 | f.WriteString("hello\nworld\n")
16 | f.Close()
17 |
18 | app := New("test", "")
19 | arg0 := app.Arg("arg0", "").String()
20 | arg1 := app.Arg("arg1", "").String()
21 |
22 | _, err = app.Parse([]string{"@" + f.Name()})
23 | assert.NoError(t, err)
24 | assert.Equal(t, "hello", *arg0)
25 | assert.Equal(t, "world", *arg1)
26 | }
27 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/parsers_test.go:
--------------------------------------------------------------------------------
1 | package kingpin
2 |
3 | import (
4 | "io/ioutil"
5 | "net"
6 | "net/url"
7 | "os"
8 |
9 | "github.com/stretchr/testify/assert"
10 |
11 | "testing"
12 | )
13 |
14 | func TestParseStrings(t *testing.T) {
15 | p := parserMixin{}
16 | v := p.Strings()
17 | p.value.Set("a")
18 | p.value.Set("b")
19 | assert.Equal(t, []string{"a", "b"}, *v)
20 | }
21 |
22 | func TestStringsStringer(t *testing.T) {
23 | target := []string{}
24 | v := newAccumulator(&target, func(v interface{}) Value { return newStringValue(v.(*string)) })
25 | v.Set("hello")
26 | v.Set("world")
27 | assert.Equal(t, "hello,world", v.String())
28 | }
29 |
30 | func TestParseStringMap(t *testing.T) {
31 | p := parserMixin{}
32 | v := p.StringMap()
33 | p.value.Set("a:b")
34 | p.value.Set("b:c")
35 | assert.Equal(t, map[string]string{"a": "b", "b": "c"}, *v)
36 | }
37 |
38 | func TestParseIP(t *testing.T) {
39 | p := parserMixin{}
40 | v := p.IP()
41 | p.value.Set("10.1.1.2")
42 | ip := net.ParseIP("10.1.1.2")
43 | assert.Equal(t, ip, *v)
44 | }
45 |
46 | func TestParseURL(t *testing.T) {
47 | p := parserMixin{}
48 | v := p.URL()
49 | p.value.Set("http://w3.org")
50 | u, err := url.Parse("http://w3.org")
51 | assert.NoError(t, err)
52 | assert.Equal(t, *u, **v)
53 | }
54 |
55 | func TestParseExistingFile(t *testing.T) {
56 | f, err := ioutil.TempFile("", "")
57 | if err != nil {
58 | t.Fatal(err)
59 | }
60 | defer f.Close()
61 | defer os.Remove(f.Name())
62 |
63 | p := parserMixin{}
64 | v := p.ExistingFile()
65 | err = p.value.Set(f.Name())
66 | assert.NoError(t, err)
67 | assert.Equal(t, f.Name(), *v)
68 | err = p.value.Set("/etc/hostsDEFINITELYMISSING")
69 | assert.Error(t, err)
70 | }
71 |
72 | func TestParseTCPAddr(t *testing.T) {
73 | p := parserMixin{}
74 | v := p.TCP()
75 | err := p.value.Set("127.0.0.1:1234")
76 | assert.NoError(t, err)
77 | expected, err := net.ResolveTCPAddr("tcp", "127.0.0.1:1234")
78 | assert.NoError(t, err)
79 | assert.Equal(t, *expected, **v)
80 | }
81 |
82 | func TestParseTCPAddrList(t *testing.T) {
83 | p := parserMixin{}
84 | _ = p.TCPList()
85 | err := p.value.Set("127.0.0.1:1234")
86 | assert.NoError(t, err)
87 | err = p.value.Set("127.0.0.1:1235")
88 | assert.NoError(t, err)
89 | assert.Equal(t, "127.0.0.1:1234,127.0.0.1:1235", p.value.String())
90 | }
91 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/repeated.go:
--------------------------------------------------------------------------------
1 | package kingpin
2 |
3 | import (
4 | "net"
5 | "time"
6 | )
7 |
8 | // This file is autogenerated by "go generate .". Do not modify.
9 |
10 | // Strings accumulates string values into a slice.
11 | func (p *parserMixin) Strings() (target *[]string) {
12 | target = new([]string)
13 | p.StringsVar(target)
14 | return
15 | }
16 |
17 | func (p *parserMixin) StringsVar(target *[]string) {
18 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newStringValue(v.(*string)) }))
19 | }
20 |
21 | // Uint64List accumulates uint64 values into a slice.
22 | func (p *parserMixin) Uint64List() (target *[]uint64) {
23 | target = new([]uint64)
24 | p.Uint64ListVar(target)
25 | return
26 | }
27 |
28 | func (p *parserMixin) Uint64ListVar(target *[]uint64) {
29 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newUint64Value(v.(*uint64)) }))
30 | }
31 |
32 | // Int64List accumulates int64 values into a slice.
33 | func (p *parserMixin) Int64List() (target *[]int64) {
34 | target = new([]int64)
35 | p.Int64ListVar(target)
36 | return
37 | }
38 |
39 | func (p *parserMixin) Int64ListVar(target *[]int64) {
40 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newInt64Value(v.(*int64)) }))
41 | }
42 |
43 | // DurationList accumulates time.Duration values into a slice.
44 | func (p *parserMixin) DurationList() (target *[]time.Duration) {
45 | target = new([]time.Duration)
46 | p.DurationListVar(target)
47 | return
48 | }
49 |
50 | func (p *parserMixin) DurationListVar(target *[]time.Duration) {
51 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newDurationValue(v.(*time.Duration)) }))
52 | }
53 |
54 | // IPList accumulates net.IP values into a slice.
55 | func (p *parserMixin) IPList() (target *[]net.IP) {
56 | target = new([]net.IP)
57 | p.IPListVar(target)
58 | return
59 | }
60 |
61 | func (p *parserMixin) IPListVar(target *[]net.IP) {
62 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newIPValue(v.(*net.IP)) }))
63 | }
64 |
65 | // TCPList accumulates *net.TCPAddr values into a slice.
66 | func (p *parserMixin) TCPList() (target *[]*net.TCPAddr) {
67 | target = new([]*net.TCPAddr)
68 | p.TCPListVar(target)
69 | return
70 | }
71 |
72 | func (p *parserMixin) TCPListVar(target *[]*net.TCPAddr) {
73 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newTCPAddrValue(v.(**net.TCPAddr)) }))
74 | }
75 |
76 | // ExistingFiles accumulates string values into a slice.
77 | func (p *parserMixin) ExistingFiles() (target *[]string) {
78 | target = new([]string)
79 | p.ExistingFilesVar(target)
80 | return
81 | }
82 |
83 | func (p *parserMixin) ExistingFilesVar(target *[]string) {
84 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newExistingFileValue(v.(*string)) }))
85 | }
86 |
87 | // ExistingDirs accumulates string values into a slice.
88 | func (p *parserMixin) ExistingDirs() (target *[]string) {
89 | target = new([]string)
90 | p.ExistingDirsVar(target)
91 | return
92 | }
93 |
94 | func (p *parserMixin) ExistingDirsVar(target *[]string) {
95 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newExistingDirValue(v.(*string)) }))
96 | }
97 |
98 | // ExistingFilesOrDirs accumulates string values into a slice.
99 | func (p *parserMixin) ExistingFilesOrDirs() (target *[]string) {
100 | target = new([]string)
101 | p.ExistingFilesOrDirsVar(target)
102 | return
103 | }
104 |
105 | func (p *parserMixin) ExistingFilesOrDirsVar(target *[]string) {
106 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newExistingFileOrDirValue(v.(*string)) }))
107 | }
108 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/repeated.json:
--------------------------------------------------------------------------------
1 | [
2 | {"name": "String", "type": "string", "plural": "Strings"},
3 | {"name": "Uint64", "type": "uint64"},
4 | {"name": "Int64", "type": "int64"},
5 | {"name": "Duration", "type": "time.Duration"},
6 | {"name": "IP", "type": "net.IP"},
7 | {"name": "TCPAddr", "Type": "*net.TCPAddr", "plural": "TCPList"},
8 | {"name": "ExistingFile", "Type": "string", "plural": "ExistingFiles"},
9 | {"name": "ExistingDir", "Type": "string", "plural": "ExistingDirs"},
10 | {"name": "ExistingFileOrDir", "Type": "string", "plural": "ExistingFilesOrDirs"}
11 | ]
12 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/usage_test.go:
--------------------------------------------------------------------------------
1 | package kingpin
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "strings"
7 | "testing"
8 |
9 | "github.com/stretchr/testify/assert"
10 | )
11 |
12 | func TestFormatTwoColumns(t *testing.T) {
13 | buf := bytes.NewBuffer(nil)
14 | formatTwoColumns(buf, 2, 2, 20, [][2]string{
15 | {"--hello", "Hello world help with something that is cool."},
16 | })
17 | expected := ` --hello Hello
18 | world
19 | help with
20 | something
21 | that is
22 | cool.
23 | `
24 | assert.Equal(t, expected, buf.String())
25 | }
26 |
27 | func TestFormatTwoColumnsWide(t *testing.T) {
28 | samples := [][2]string{
29 | {strings.Repeat("x", 19), "19 chars"},
30 | {strings.Repeat("x", 20), "20 chars"}}
31 | buf := bytes.NewBuffer(nil)
32 | formatTwoColumns(buf, 0, 0, 200, samples)
33 | fmt.Println(buf.String())
34 | expected := `xxxxxxxxxxxxxxxxxxx19 chars
35 | xxxxxxxxxxxxxxxxxxxx
36 | 20 chars
37 | `
38 | assert.Equal(t, expected, buf.String())
39 | }
40 |
41 | func TestHiddenCommand(t *testing.T) {
42 | templates := []struct{ name, template string }{
43 | {"default", DefaultUsageTemplate},
44 | {"Compact", CompactUsageTemplate},
45 | {"Long", LongHelpTemplate},
46 | {"Man", ManPageTemplate},
47 | }
48 |
49 | var buf bytes.Buffer
50 | t.Log("1")
51 |
52 | a := New("test", "Test").Writer(&buf).Terminate(nil)
53 | a.Command("visible", "visible")
54 | a.Command("hidden", "hidden").Hidden()
55 |
56 | for _, tp := range templates {
57 | buf.Reset()
58 | a.UsageTemplate(tp.template)
59 | a.Parse(nil)
60 | // a.Parse([]string{"--help"})
61 | usage := buf.String()
62 | t.Logf("Usage for %s is:\n%s\n", tp.name, usage)
63 |
64 | assert.NotContains(t, usage, "hidden")
65 | assert.Contains(t, usage, "visible")
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/check.v1/.gitignore:
--------------------------------------------------------------------------------
1 | _*
2 | *.swp
3 | *.[568]
4 | [568].out
5 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/check.v1/LICENSE:
--------------------------------------------------------------------------------
1 | Gocheck - A rich testing framework for Go
2 |
3 | Copyright (c) 2010-2013 Gustavo Niemeyer
4 |
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions are met:
9 |
10 | 1. Redistributions of source code must retain the above copyright notice, this
11 | list of conditions and the following disclaimer.
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/check.v1/README.md:
--------------------------------------------------------------------------------
1 | Instructions
2 | ============
3 |
4 | Install the package with:
5 |
6 | go get gopkg.in/check.v1
7 |
8 | Import it with:
9 |
10 | import "gopkg.in/check.v1"
11 |
12 | and use _check_ as the package name inside the code.
13 |
14 | For more details, visit the project page:
15 |
16 | * http://labix.org/gocheck
17 |
18 | and the API documentation:
19 |
20 | * https://gopkg.in/check.v1
21 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/check.v1/TODO:
--------------------------------------------------------------------------------
1 | - Assert(slice, Contains, item)
2 | - Parallel test support
3 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/check.v1/benchmark_test.go:
--------------------------------------------------------------------------------
1 | // These tests verify the test running logic.
2 |
3 | package check_test
4 |
5 | import (
6 | "time"
7 | . "gopkg.in/check.v1"
8 | )
9 |
10 | var benchmarkS = Suite(&BenchmarkS{})
11 |
12 | type BenchmarkS struct{}
13 |
14 | func (s *BenchmarkS) TestCountSuite(c *C) {
15 | suitesRun += 1
16 | }
17 |
18 | func (s *BenchmarkS) TestBasicTestTiming(c *C) {
19 | helper := FixtureHelper{sleepOn: "Test1", sleep: 1000000 * time.Nanosecond}
20 | output := String{}
21 | runConf := RunConf{Output: &output, Verbose: true}
22 | Run(&helper, &runConf)
23 |
24 | expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test1\t0\\.001s\n" +
25 | "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test2\t0\\.000s\n"
26 | c.Assert(output.value, Matches, expected)
27 | }
28 |
29 | func (s *BenchmarkS) TestStreamTestTiming(c *C) {
30 | helper := FixtureHelper{sleepOn: "SetUpSuite", sleep: 1000000 * time.Nanosecond}
31 | output := String{}
32 | runConf := RunConf{Output: &output, Stream: true}
33 | Run(&helper, &runConf)
34 |
35 | expected := "(?s).*\nPASS: check_test\\.go:[0-9]+: FixtureHelper\\.SetUpSuite\t *0\\.001s\n.*"
36 | c.Assert(output.value, Matches, expected)
37 | }
38 |
39 | func (s *BenchmarkS) TestBenchmark(c *C) {
40 | helper := FixtureHelper{sleep: 100000}
41 | output := String{}
42 | runConf := RunConf{
43 | Output: &output,
44 | Benchmark: true,
45 | BenchmarkTime: 10000000,
46 | Filter: "Benchmark1",
47 | }
48 | Run(&helper, &runConf)
49 | c.Check(helper.calls[0], Equals, "SetUpSuite")
50 | c.Check(helper.calls[1], Equals, "SetUpTest")
51 | c.Check(helper.calls[2], Equals, "Benchmark1")
52 | c.Check(helper.calls[3], Equals, "TearDownTest")
53 | c.Check(helper.calls[4], Equals, "SetUpTest")
54 | c.Check(helper.calls[5], Equals, "Benchmark1")
55 | c.Check(helper.calls[6], Equals, "TearDownTest")
56 | // ... and more.
57 |
58 | expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Benchmark1\t *100\t *[12][0-9]{5} ns/op\n"
59 | c.Assert(output.value, Matches, expected)
60 | }
61 |
62 | func (s *BenchmarkS) TestBenchmarkBytes(c *C) {
63 | helper := FixtureHelper{sleep: 100000}
64 | output := String{}
65 | runConf := RunConf{
66 | Output: &output,
67 | Benchmark: true,
68 | BenchmarkTime: 10000000,
69 | Filter: "Benchmark2",
70 | }
71 | Run(&helper, &runConf)
72 |
73 | expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Benchmark2\t *100\t *[12][0-9]{5} ns/op\t *[4-9]\\.[0-9]{2} MB/s\n"
74 | c.Assert(output.value, Matches, expected)
75 | }
76 |
77 | func (s *BenchmarkS) TestBenchmarkMem(c *C) {
78 | helper := FixtureHelper{sleep: 100000}
79 | output := String{}
80 | runConf := RunConf{
81 | Output: &output,
82 | Benchmark: true,
83 | BenchmarkMem: true,
84 | BenchmarkTime: 10000000,
85 | Filter: "Benchmark3",
86 | }
87 | Run(&helper, &runConf)
88 |
89 | expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Benchmark3\t *100\t *[12][0-9]{5} ns/op\t *[0-9]+ B/op\t *[1-9] allocs/op\n"
90 | c.Assert(output.value, Matches, expected)
91 | }
92 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/check.v1/bootstrap_test.go:
--------------------------------------------------------------------------------
1 | // These initial tests are for bootstrapping. They verify that we can
2 | // basically use the testing infrastructure itself to check if the test
3 | // system is working.
4 | //
5 | // These tests use will break down the test runner badly in case of
6 | // errors because if they simply fail, we can't be sure the developer
7 | // will ever see anything (because failing means the failing system
8 | // somehow isn't working! :-)
9 | //
10 | // Do not assume *any* internal functionality works as expected besides
11 | // what's actually tested here.
12 |
13 | package check_test
14 |
15 | import (
16 | "fmt"
17 | "gopkg.in/check.v1"
18 | "strings"
19 | )
20 |
21 | type BootstrapS struct{}
22 |
23 | var boostrapS = check.Suite(&BootstrapS{})
24 |
25 | func (s *BootstrapS) TestCountSuite(c *check.C) {
26 | suitesRun += 1
27 | }
28 |
29 | func (s *BootstrapS) TestFailedAndFail(c *check.C) {
30 | if c.Failed() {
31 | critical("c.Failed() must be false first!")
32 | }
33 | c.Fail()
34 | if !c.Failed() {
35 | critical("c.Fail() didn't put the test in a failed state!")
36 | }
37 | c.Succeed()
38 | }
39 |
40 | func (s *BootstrapS) TestFailedAndSucceed(c *check.C) {
41 | c.Fail()
42 | c.Succeed()
43 | if c.Failed() {
44 | critical("c.Succeed() didn't put the test back in a non-failed state")
45 | }
46 | }
47 |
48 | func (s *BootstrapS) TestLogAndGetTestLog(c *check.C) {
49 | c.Log("Hello there!")
50 | log := c.GetTestLog()
51 | if log != "Hello there!\n" {
52 | critical(fmt.Sprintf("Log() or GetTestLog() is not working! Got: %#v", log))
53 | }
54 | }
55 |
56 | func (s *BootstrapS) TestLogfAndGetTestLog(c *check.C) {
57 | c.Logf("Hello %v", "there!")
58 | log := c.GetTestLog()
59 | if log != "Hello there!\n" {
60 | critical(fmt.Sprintf("Logf() or GetTestLog() is not working! Got: %#v", log))
61 | }
62 | }
63 |
64 | func (s *BootstrapS) TestRunShowsErrors(c *check.C) {
65 | output := String{}
66 | check.Run(&FailHelper{}, &check.RunConf{Output: &output})
67 | if strings.Index(output.value, "Expected failure!") == -1 {
68 | critical(fmt.Sprintf("RunWithWriter() output did not contain the "+
69 | "expected failure! Got: %#v",
70 | output.value))
71 | }
72 | }
73 |
74 | func (s *BootstrapS) TestRunDoesntShowSuccesses(c *check.C) {
75 | output := String{}
76 | check.Run(&SuccessHelper{}, &check.RunConf{Output: &output})
77 | if strings.Index(output.value, "Expected success!") != -1 {
78 | critical(fmt.Sprintf("RunWithWriter() output contained a successful "+
79 | "test! Got: %#v",
80 | output.value))
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/check.v1/export_test.go:
--------------------------------------------------------------------------------
1 | package check
2 |
3 | func PrintLine(filename string, line int) (string, error) {
4 | return printLine(filename, line)
5 | }
6 |
7 | func Indent(s, with string) string {
8 | return indent(s, with)
9 | }
10 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/check.v1/printer_test.go:
--------------------------------------------------------------------------------
1 | package check_test
2 |
3 | import (
4 | . "gopkg.in/check.v1"
5 | )
6 |
7 | var _ = Suite(&PrinterS{})
8 |
9 | type PrinterS struct{}
10 |
11 | func (s *PrinterS) TestCountSuite(c *C) {
12 | suitesRun += 1
13 | }
14 |
15 | var printTestFuncLine int
16 |
17 | func init() {
18 | printTestFuncLine = getMyLine() + 3
19 | }
20 |
21 | func printTestFunc() {
22 | println(1) // Comment1
23 | if 2 == 2 { // Comment2
24 | println(3) // Comment3
25 | }
26 | switch 5 {
27 | case 6: println(6) // Comment6
28 | println(7)
29 | }
30 | switch interface{}(9).(type) {// Comment9
31 | case int: println(10)
32 | println(11)
33 | }
34 | select {
35 | case <-(chan bool)(nil): println(14)
36 | println(15)
37 | default: println(16)
38 | println(17)
39 | }
40 | println(19,
41 | 20)
42 | _ = func() { println(21)
43 | println(22)
44 | }
45 | println(24, func() {
46 | println(25)
47 | })
48 | // Leading comment
49 | // with multiple lines.
50 | println(29) // Comment29
51 | }
52 |
53 | var printLineTests = []struct {
54 | line int
55 | output string
56 | }{
57 | {1, "println(1) // Comment1"},
58 | {2, "if 2 == 2 { // Comment2\n ...\n}"},
59 | {3, "println(3) // Comment3"},
60 | {5, "switch 5 {\n...\n}"},
61 | {6, "case 6:\n println(6) // Comment6\n ..."},
62 | {7, "println(7)"},
63 | {9, "switch interface{}(9).(type) { // Comment9\n...\n}"},
64 | {10, "case int:\n println(10)\n ..."},
65 | {14, "case <-(chan bool)(nil):\n println(14)\n ..."},
66 | {15, "println(15)"},
67 | {16, "default:\n println(16)\n ..."},
68 | {17, "println(17)"},
69 | {19, "println(19,\n 20)"},
70 | {20, "println(19,\n 20)"},
71 | {21, "_ = func() {\n println(21)\n println(22)\n}"},
72 | {22, "println(22)"},
73 | {24, "println(24, func() {\n println(25)\n})"},
74 | {25, "println(25)"},
75 | {26, "println(24, func() {\n println(25)\n})"},
76 | {29, "// Leading comment\n// with multiple lines.\nprintln(29) // Comment29"},
77 | }
78 |
79 | func (s *PrinterS) TestPrintLine(c *C) {
80 | for _, test := range printLineTests {
81 | output, err := PrintLine("printer_test.go", printTestFuncLine+test.line)
82 | c.Assert(err, IsNil)
83 | c.Assert(output, Equals, test.output)
84 | }
85 | }
86 |
87 | var indentTests = []struct {
88 | in, out string
89 | }{
90 | {"", ""},
91 | {"\n", "\n"},
92 | {"a", ">>>a"},
93 | {"a\n", ">>>a\n"},
94 | {"a\nb", ">>>a\n>>>b"},
95 | {" ", ">>> "},
96 | }
97 |
98 | func (s *PrinterS) TestIndent(c *C) {
99 | for _, test := range indentTests {
100 | out := Indent(test.in, ">>>")
101 | c.Assert(out, Equals, test.out)
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/yaml.v2/LICENSE.libyaml:
--------------------------------------------------------------------------------
1 | The following files were ported to Go from C files of libyaml, and thus
2 | are still covered by their original copyright and license:
3 |
4 | apic.go
5 | emitterc.go
6 | parserc.go
7 | readerc.go
8 | scannerc.go
9 | writerc.go
10 | yamlh.go
11 | yamlprivateh.go
12 |
13 | Copyright (c) 2006 Kirill Simonov
14 |
15 | Permission is hereby granted, free of charge, to any person obtaining a copy of
16 | this software and associated documentation files (the "Software"), to deal in
17 | the Software without restriction, including without limitation the rights to
18 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
19 | of the Software, and to permit persons to whom the Software is furnished to do
20 | so, subject to the following conditions:
21 |
22 | The above copyright notice and this permission notice shall be included in all
23 | copies or substantial portions of the Software.
24 |
25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 | SOFTWARE.
32 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/yaml.v2/README.md:
--------------------------------------------------------------------------------
1 | # YAML support for the Go language
2 |
3 | Introduction
4 | ------------
5 |
6 | The yaml package enables Go programs to comfortably encode and decode YAML
7 | values. It was developed within [Canonical](https://www.canonical.com) as
8 | part of the [juju](https://juju.ubuntu.com) project, and is based on a
9 | pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML)
10 | C library to parse and generate YAML data quickly and reliably.
11 |
12 | Compatibility
13 | -------------
14 |
15 | The yaml package supports most of YAML 1.1 and 1.2, including support for
16 | anchors, tags, map merging, etc. Multi-document unmarshalling is not yet
17 | implemented, and base-60 floats from YAML 1.1 are purposefully not
18 | supported since they're a poor design and are gone in YAML 1.2.
19 |
20 | Installation and usage
21 | ----------------------
22 |
23 | The import path for the package is *gopkg.in/yaml.v2*.
24 |
25 | To install it, run:
26 |
27 | go get gopkg.in/yaml.v2
28 |
29 | API documentation
30 | -----------------
31 |
32 | If opened in a browser, the import path itself leads to the API documentation:
33 |
34 | * [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2)
35 |
36 | API stability
37 | -------------
38 |
39 | The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in).
40 |
41 |
42 | License
43 | -------
44 |
45 | The yaml package is licensed under the LGPL with an exception that allows it to be linked statically. Please see the LICENSE file for details.
46 |
47 |
48 | Example
49 | -------
50 |
51 | ```Go
52 | package main
53 |
54 | import (
55 | "fmt"
56 | "log"
57 |
58 | "gopkg.in/yaml.v2"
59 | )
60 |
61 | var data = `
62 | a: Easy!
63 | b:
64 | c: 2
65 | d: [3, 4]
66 | `
67 |
68 | type T struct {
69 | A string
70 | B struct{C int; D []int ",flow"}
71 | }
72 |
73 | func main() {
74 | t := T{}
75 |
76 | err := yaml.Unmarshal([]byte(data), &t)
77 | if err != nil {
78 | log.Fatalf("error: %v", err)
79 | }
80 | fmt.Printf("--- t:\n%v\n\n", t)
81 |
82 | d, err := yaml.Marshal(&t)
83 | if err != nil {
84 | log.Fatalf("error: %v", err)
85 | }
86 | fmt.Printf("--- t dump:\n%s\n\n", string(d))
87 |
88 | m := make(map[interface{}]interface{})
89 |
90 | err = yaml.Unmarshal([]byte(data), &m)
91 | if err != nil {
92 | log.Fatalf("error: %v", err)
93 | }
94 | fmt.Printf("--- m:\n%v\n\n", m)
95 |
96 | d, err = yaml.Marshal(&m)
97 | if err != nil {
98 | log.Fatalf("error: %v", err)
99 | }
100 | fmt.Printf("--- m dump:\n%s\n\n", string(d))
101 | }
102 | ```
103 |
104 | This example will generate the following output:
105 |
106 | ```
107 | --- t:
108 | {Easy! {2 [3 4]}}
109 |
110 | --- t dump:
111 | a: Easy!
112 | b:
113 | c: 2
114 | d: [3, 4]
115 |
116 |
117 | --- m:
118 | map[a:Easy! b:map[c:2 d:[3 4]]]
119 |
120 | --- m dump:
121 | a: Easy!
122 | b:
123 | c: 2
124 | d:
125 | - 3
126 | - 4
127 | ```
128 |
129 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/yaml.v2/sorter.go:
--------------------------------------------------------------------------------
1 | package yaml
2 |
3 | import (
4 | "reflect"
5 | "unicode"
6 | )
7 |
8 | type keyList []reflect.Value
9 |
10 | func (l keyList) Len() int { return len(l) }
11 | func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
12 | func (l keyList) Less(i, j int) bool {
13 | a := l[i]
14 | b := l[j]
15 | ak := a.Kind()
16 | bk := b.Kind()
17 | for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() {
18 | a = a.Elem()
19 | ak = a.Kind()
20 | }
21 | for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() {
22 | b = b.Elem()
23 | bk = b.Kind()
24 | }
25 | af, aok := keyFloat(a)
26 | bf, bok := keyFloat(b)
27 | if aok && bok {
28 | if af != bf {
29 | return af < bf
30 | }
31 | if ak != bk {
32 | return ak < bk
33 | }
34 | return numLess(a, b)
35 | }
36 | if ak != reflect.String || bk != reflect.String {
37 | return ak < bk
38 | }
39 | ar, br := []rune(a.String()), []rune(b.String())
40 | for i := 0; i < len(ar) && i < len(br); i++ {
41 | if ar[i] == br[i] {
42 | continue
43 | }
44 | al := unicode.IsLetter(ar[i])
45 | bl := unicode.IsLetter(br[i])
46 | if al && bl {
47 | return ar[i] < br[i]
48 | }
49 | if al || bl {
50 | return bl
51 | }
52 | var ai, bi int
53 | var an, bn int64
54 | for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ {
55 | an = an*10 + int64(ar[ai]-'0')
56 | }
57 | for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ {
58 | bn = bn*10 + int64(br[bi]-'0')
59 | }
60 | if an != bn {
61 | return an < bn
62 | }
63 | if ai != bi {
64 | return ai < bi
65 | }
66 | return ar[i] < br[i]
67 | }
68 | return len(ar) < len(br)
69 | }
70 |
71 | // keyFloat returns a float value for v if it is a number/bool
72 | // and whether it is a number/bool or not.
73 | func keyFloat(v reflect.Value) (f float64, ok bool) {
74 | switch v.Kind() {
75 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
76 | return float64(v.Int()), true
77 | case reflect.Float32, reflect.Float64:
78 | return v.Float(), true
79 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
80 | return float64(v.Uint()), true
81 | case reflect.Bool:
82 | if v.Bool() {
83 | return 1, true
84 | }
85 | return 0, true
86 | }
87 | return 0, false
88 | }
89 |
90 | // numLess returns whether a < b.
91 | // a and b must necessarily have the same kind.
92 | func numLess(a, b reflect.Value) bool {
93 | switch a.Kind() {
94 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
95 | return a.Int() < b.Int()
96 | case reflect.Float32, reflect.Float64:
97 | return a.Float() < b.Float()
98 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
99 | return a.Uint() < b.Uint()
100 | case reflect.Bool:
101 | return !a.Bool() && b.Bool()
102 | }
103 | panic("not a number")
104 | }
105 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/yaml.v2/suite_test.go:
--------------------------------------------------------------------------------
1 | package yaml_test
2 |
3 | import (
4 | . "gopkg.in/check.v1"
5 | "testing"
6 | )
7 |
8 | func Test(t *testing.T) { TestingT(t) }
9 |
10 | type S struct{}
11 |
12 | var _ = Suite(&S{})
13 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/gopkg.in/yaml.v2/writerc.go:
--------------------------------------------------------------------------------
1 | package yaml
2 |
3 | // Set the writer error and return false.
4 | func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool {
5 | emitter.error = yaml_WRITER_ERROR
6 | emitter.problem = problem
7 | return false
8 | }
9 |
10 | // Flush the output buffer.
11 | func yaml_emitter_flush(emitter *yaml_emitter_t) bool {
12 | if emitter.write_handler == nil {
13 | panic("write handler not set")
14 | }
15 |
16 | // Check if the buffer is empty.
17 | if emitter.buffer_pos == 0 {
18 | return true
19 | }
20 |
21 | // If the output encoding is UTF-8, we don't need to recode the buffer.
22 | if emitter.encoding == yaml_UTF8_ENCODING {
23 | if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil {
24 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
25 | }
26 | emitter.buffer_pos = 0
27 | return true
28 | }
29 |
30 | // Recode the buffer into the raw buffer.
31 | var low, high int
32 | if emitter.encoding == yaml_UTF16LE_ENCODING {
33 | low, high = 0, 1
34 | } else {
35 | high, low = 1, 0
36 | }
37 |
38 | pos := 0
39 | for pos < emitter.buffer_pos {
40 | // See the "reader.c" code for more details on UTF-8 encoding. Note
41 | // that we assume that the buffer contains a valid UTF-8 sequence.
42 |
43 | // Read the next UTF-8 character.
44 | octet := emitter.buffer[pos]
45 |
46 | var w int
47 | var value rune
48 | switch {
49 | case octet&0x80 == 0x00:
50 | w, value = 1, rune(octet&0x7F)
51 | case octet&0xE0 == 0xC0:
52 | w, value = 2, rune(octet&0x1F)
53 | case octet&0xF0 == 0xE0:
54 | w, value = 3, rune(octet&0x0F)
55 | case octet&0xF8 == 0xF0:
56 | w, value = 4, rune(octet&0x07)
57 | }
58 | for k := 1; k < w; k++ {
59 | octet = emitter.buffer[pos+k]
60 | value = (value << 6) + (rune(octet) & 0x3F)
61 | }
62 | pos += w
63 |
64 | // Write the character.
65 | if value < 0x10000 {
66 | var b [2]byte
67 | b[high] = byte(value >> 8)
68 | b[low] = byte(value & 0xFF)
69 | emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1])
70 | } else {
71 | // Write the character using a surrogate pair (check "reader.c").
72 | var b [4]byte
73 | value -= 0x10000
74 | b[high] = byte(0xD8 + (value >> 18))
75 | b[low] = byte((value >> 10) & 0xFF)
76 | b[high+2] = byte(0xDC + ((value >> 8) & 0xFF))
77 | b[low+2] = byte(value & 0xFF)
78 | emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1], b[2], b[3])
79 | }
80 | }
81 |
82 | // Write the raw buffer.
83 | if err := emitter.write_handler(emitter, emitter.raw_buffer); err != nil {
84 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
85 | }
86 | emitter.buffer_pos = 0
87 | emitter.raw_buffer = emitter.raw_buffer[:0]
88 | return true
89 | }
90 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | default:
3 | @godep go build
4 | @ls -ltrh
5 |
6 | setup:
7 | @echo Installing developer tooling, godep and reflex
8 | go get github.com/tools/godep
9 | go get github.com/cespare/reflex/...
10 | go get golang.org/x/tools/cmd/cover
11 | go get github.com/vektra/mockery/...
12 |
13 | .goxc.ok:
14 | @echo Installing crossbuild tooling. This will take a while...
15 | go get github.com/laher/goxc
16 | goxc -t
17 | touch .goxc.ok
18 |
19 | watch:
20 | @reflex -g '*.go' make test
21 |
22 | test:
23 | @godep go test -coverprofile=c.out
24 |
25 | coverage: test
26 | @godep go tool cover -html=c.out
27 |
28 | bump:
29 | @goxc bump
30 |
31 | release: .goxc.ok
32 | godep save
33 | goxc
34 |
35 | mocks:
36 | @mockery -name Converter
37 |
38 | brew_sha:
39 | @shasum -a 256 $(ver)/blade_$(ver)_darwin_amd64.zip
40 | .PHONY: default test setup release watch coverage mocks bump brew_sha
41 |
42 |
--------------------------------------------------------------------------------
/blade.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | log "github.com/Sirupsen/logrus"
5 | "os"
6 | "path"
7 | )
8 |
9 | type Blade struct {
10 | Template string `yaml:"template"`
11 | Interpolation string `yaml:"interpolation"`
12 | Source string `yaml:"source"`
13 | Out string `yaml:"out"`
14 | IncludeContents bool `yaml:"contents"`
15 | DryRun bool
16 | Mount string `yaml:"mount"`
17 | }
18 |
19 | func (b *Blade) Run() {
20 | if b.Mount != "" {
21 | template := path.Join(b.Mount, "Contents.json")
22 | if _, err := os.Stat(template); os.IsNotExist(err) {
23 | log.Fatalf("A mount must point to an image catalog (Contents.json missing)")
24 | }
25 | b.Out = b.Mount
26 | b.Template = template
27 | }
28 |
29 | c := NewContentsFromFile(b.Template)
30 |
31 | var converter Converter
32 |
33 | if b.DryRun {
34 | converter = NewDryrunConverter()
35 | } else {
36 | cv := NewResizeConverter()
37 | cv.Interpolation = b.Interpolation
38 | converter = cv
39 | }
40 |
41 | r := NewRunner(c, converter, b.Source)
42 | if b.Out != "" {
43 | err := os.MkdirAll(b.Out, 0755)
44 | if err != nil {
45 | log.Fatalf("Cannot create output directory '%s' (%s)", b.Out, err)
46 | }
47 | r.OutDir = b.Out
48 | }
49 |
50 | if b.IncludeContents {
51 | r.GenerateContents = true
52 | }
53 |
54 | r.run()
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/bladefile.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "gopkg.in/yaml.v2"
5 | "io/ioutil"
6 | "os"
7 | )
8 |
9 | type Bladefile struct {
10 | Blades []Blade `yaml:"blades"`
11 | }
12 |
13 | func (bf *Bladefile) Exists() bool {
14 | if _, err := os.Stat("Bladefile"); os.IsNotExist(err) {
15 | return false
16 | }
17 | return true
18 | }
19 |
20 | func (bf *Bladefile) Load() error {
21 | data, err := ioutil.ReadFile("Bladefile")
22 | if err != nil {
23 | return err
24 | }
25 |
26 | err = yaml.Unmarshal([]byte(data), bf)
27 | if err != nil {
28 | return err
29 | }
30 | return nil
31 | }
32 |
--------------------------------------------------------------------------------
/contents.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | log "github.com/Sirupsen/logrus"
7 | "io/ioutil"
8 | "strconv"
9 | "strings"
10 | )
11 |
12 | type Contents struct {
13 | Images []ContentsImage `json:"images"`
14 | Info struct {
15 | Author string `json:"author"`
16 | Version int `json:"version"`
17 | } `json:"info"`
18 | }
19 |
20 | type ContentsImage struct {
21 | Filename string `json:"filename"`
22 | Idiom string `json:"idiom"`
23 | Scale string `json:"scale"`
24 | Size string `json:"size"`
25 | ScreenWidth string `json:"screenWidth,omitempty"`
26 | }
27 |
28 | func (ci *ContentsImage) GetScale() int {
29 | if ci.Scale == "" {
30 | return 1
31 | }
32 |
33 | factor, err := strconv.Atoi(ci.Scale[0:1])
34 | if err != nil {
35 | log.Fatalf("Converter(Resize): cannot parse scale %s (%s).", ci.Size, err)
36 | }
37 | return factor
38 | }
39 |
40 | func (ci *ContentsImage) GetSize() (float64, float64) {
41 | a := strings.Split(ci.Size, "x")
42 | w, err := strconv.ParseFloat(a[0], 64)
43 | if err != nil {
44 | log.Fatalf("Converter(Resize): cannot parse width %s (%s).", a[0], err)
45 | }
46 | h, err := strconv.ParseFloat(a[1], 64)
47 | if err != nil {
48 | log.Fatalf("Converter(Resize): cannot parse height %s (%s).", a[0], err)
49 | }
50 |
51 | return w, h
52 | }
53 |
54 | func (ci *ContentsImage) BuildFilename(base string, rect Rect) string {
55 | scale := ci.GetScale()
56 | return fmt.Sprintf("%s-%s-%d@%dx.png", base, ci.Idiom, int(float64(rect.Width)/float64(scale)), scale)
57 | }
58 |
59 | func NewContentsFromFile(path string) *Contents {
60 | data, err := ioutil.ReadFile(path)
61 | if err != nil {
62 | log.Fatalf("Contents: cannot read from %s (%s).", path, err)
63 | }
64 | return NewContentsFromString(data)
65 | }
66 |
67 | func NewContentsFromString(data []byte) *Contents {
68 | var contents Contents
69 | json.Unmarshal(data, &contents)
70 | return &contents
71 | }
72 |
73 | func (c *Contents) WriteToFile(file string) error {
74 | b, err := json.MarshalIndent(c, "", " ")
75 | if err != nil {
76 | return err
77 | }
78 |
79 | err = ioutil.WriteFile(file, b, 0644)
80 | if err != nil {
81 | return err
82 | }
83 | return nil
84 | }
85 |
--------------------------------------------------------------------------------
/contents_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | . "gopkg.in/check.v1"
5 | )
6 |
7 | type ContentsSuite struct {
8 | }
9 |
10 | var _ = Suite(&ContentsSuite{})
11 |
12 | func (cs *ContentsSuite) Test_should_build_filename(c *C) {
13 | ci := ContentsImage{}
14 | fname := ci.BuildFilename("base", Rect{42, 24})
15 | c.Check(fname, Equals, "base--42@1x.png")
16 |
17 | ci.Idiom = "iphone"
18 | fname = ci.BuildFilename("base", Rect{42, 24})
19 | c.Check(fname, Equals, "base-iphone-42@1x.png")
20 |
21 | ci.Scale = "1x"
22 | fname = ci.BuildFilename("base", Rect{42, 24})
23 | c.Check(fname, Equals, "base-iphone-42@1x.png")
24 |
25 | ci.Scale = "3x"
26 | fname = ci.BuildFilename("base", Rect{42, 24})
27 | c.Check(fname, Equals, "base-iphone-14@3x.png")
28 | }
29 |
30 | func (cs *ContentsSuite) Test_should_get_size(c *C) {
31 | ci := ContentsImage{}
32 | ci.Size = "42x24"
33 | w, h := ci.GetSize()
34 | c.Check(w, Equals, 42.0)
35 | c.Check(h, Equals, 24.0)
36 |
37 | ci.Size = "27.5x24.5"
38 | w, h = ci.GetSize()
39 | c.Check(w, Equals, 27.5)
40 | c.Check(h, Equals, 24.5)
41 |
42 | ci.Size = "27x24.5"
43 | w, h = ci.GetSize()
44 | c.Check(w, Equals, 27.0)
45 | c.Check(h, Equals, 24.5)
46 |
47 | ci.Size = "27.5x24"
48 | w, h = ci.GetSize()
49 | c.Check(w, Equals, 27.5)
50 | c.Check(h, Equals, 24.0)
51 | }
52 |
53 | func (cs *ContentsSuite) Test_should_get_scale(c *C) {
54 | ci := ContentsImage{}
55 | ci.Scale = "3x"
56 | s := ci.GetScale()
57 | c.Check(s, Equals, 3)
58 |
59 | ci.Scale = ""
60 | s = ci.GetScale()
61 | c.Check(s, Equals, 1)
62 | }
63 |
--------------------------------------------------------------------------------
/converter.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Converter interface {
4 | Convert(string, string, Rect) error
5 | Size(string) (Rect, error)
6 | }
7 |
--------------------------------------------------------------------------------
/dimensions.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "math"
5 | )
6 |
7 | type Dimensions struct {
8 | }
9 |
10 | type Rect struct {
11 | Width float64
12 | Height float64
13 | }
14 |
15 | func NewDimensions() *Dimensions {
16 | return &Dimensions{}
17 | }
18 |
19 | func (d *Dimensions) Compute(contents *Contents, meta *ContentsImage, sourceRect Rect) Rect {
20 | if meta.Size != "" {
21 | return dimensionFromSize(meta)
22 | }
23 |
24 | //
25 | // If we don't get an explicit size we have to compute it.
26 | // 1. Find the biggest scale factor in the requested idiom group
27 | // 2. Assuming the source image is the largest possible image we have,
28 | // compute a reduction scale factor (current / highest), and scale down the source.
29 | // e.g if highest scale factor for iPad is x3, and the current image request is x2,
30 | // we need to scale down the source image by 2/3.
31 | //
32 |
33 | // an idiom is just meta.Idiom, however with Apple Watch we have the same idiom with different
34 | // screen sizes. Feels like Apple patched this abstraction out with 'screenWidth'. So let's
35 | // compose a new idiom built from the original idiom and this screenWidth concept.
36 | idiom := meta.Idiom + meta.ScreenWidth
37 | highestFactor := 1.0
38 |
39 | for _, m := range contents.Images {
40 | if m.Idiom+m.ScreenWidth == idiom {
41 | highestFactor = math.Max(float64(highestFactor), float64(m.GetScale()))
42 | }
43 | }
44 |
45 | scaleDownFactor := float64(meta.GetScale()) / highestFactor
46 | //log.Printf("%v scale factor: %1.2f computed from %d and highest %2.0f", meta, scaleDownFactor, meta.GetScale(), highestFactor)
47 | return Rect{
48 | float64(sourceRect.Width) * scaleDownFactor,
49 | float64(sourceRect.Height) * scaleDownFactor,
50 | }
51 |
52 | }
53 |
54 | func dimensionFromSize(c *ContentsImage) Rect {
55 | w, h := c.GetSize()
56 | factor := float64(c.GetScale())
57 |
58 | return Rect{Width: factor * w, Height: factor * h}
59 | }
60 |
--------------------------------------------------------------------------------
/dimensions_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | . "gopkg.in/check.v1"
5 | )
6 |
7 | type DimensionsSuite struct {
8 | dim *Dimensions
9 | }
10 |
11 | var _ = Suite(&DimensionsSuite{
12 | dim: NewDimensions(),
13 | })
14 |
15 | func (s *DimensionsSuite) Test_should_compute_size_based_on_existing_scales_given_no_size(c *C) {
16 | contents := NewContentsFromFile("fixtures/contents-no-sizes.json")
17 | // iphone
18 | r := s.dim.Compute(
19 | contents,
20 | &contents.Images[0],
21 | Rect{60, 60},
22 | )
23 | c.Check(r.Width, Equals, 40.0)
24 | c.Check(r.Height, Equals, 40.0)
25 |
26 | // ipad
27 | r = s.dim.Compute(
28 | contents,
29 | &contents.Images[2],
30 | Rect{60, 60},
31 | )
32 | c.Check(r.Width, Equals, 60.0)
33 | c.Check(r.Height, Equals, 60.0)
34 |
35 | }
36 |
37 | func (s *DimensionsSuite) Test_should_use_size_and_scale_when_given_size_explicitely(c *C) {
38 | contents := NewContentsFromFile("fixtures/contents-appicon.json")
39 | r := s.dim.Compute(
40 | contents,
41 | &contents.Images[0],
42 | Rect{42, 42},
43 | )
44 | c.Check(r.Width, Equals, 29*2.0)
45 | c.Check(r.Height, Equals, 29*2.0)
46 | }
47 |
--------------------------------------------------------------------------------
/docs/blade-s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/docs/blade-s.png
--------------------------------------------------------------------------------
/docs/blade-walkthrough.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/docs/blade-walkthrough.gif
--------------------------------------------------------------------------------
/docs/blade.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/docs/blade.gif
--------------------------------------------------------------------------------
/dryrun_converter.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | log "github.com/Sirupsen/logrus"
5 | )
6 |
7 | type DryrunConverter struct {
8 | }
9 |
10 | func NewDryrunConverter() *DryrunConverter {
11 | return &DryrunConverter{}
12 | }
13 | func (d *DryrunConverter) Size(in string) (Rect, error) {
14 | return Rect{42, 42}, nil
15 | }
16 |
17 | func (d *DryrunConverter) Convert(in string, out string, toRect Rect) error {
18 | log.Infof("(dryrun) Converting %s to %s using %v\n", in, out, toRect)
19 | return nil
20 | }
21 |
--------------------------------------------------------------------------------
/fixtures/contents-appicon.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "29x29",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-Small@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "29x29",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-Small@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "40x40",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-40@2x.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "size" : "40x40",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-40@3x.png",
25 | "scale" : "3x"
26 | },
27 | {
28 | "size" : "60x60",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-60@2x.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "size" : "60x60",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-60@3x.png",
37 | "scale" : "3x"
38 | },
39 | {
40 | "size" : "29x29",
41 | "idiom" : "ipad",
42 | "filename" : "Icon-Small.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "29x29",
47 | "idiom" : "ipad",
48 | "filename" : "Icon-Small@2x-1.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "40x40",
53 | "idiom" : "ipad",
54 | "filename" : "Icon-40.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "40x40",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-40@2x-1.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "size" : "76x76",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-76.png",
67 | "scale" : "1x"
68 | },
69 | {
70 | "size" : "76x76",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-76@2x.png",
73 | "scale" : "2x"
74 | }
75 | ],
76 | "info" : {
77 | "version" : 1,
78 | "author" : "xcode"
79 | }
80 | }
--------------------------------------------------------------------------------
/fixtures/contents-no-sizes-no-files.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x"
6 | },
7 | {
8 | "idiom" : "iphone",
9 | "scale" : "3x"
10 | },
11 | {
12 | "idiom" : "iphone",
13 | "scale" : "2x"
14 | },
15 | {
16 | "idiom" : "iphone",
17 | "scale" : "3x"
18 | },
19 | {
20 | "idiom" : "iphone",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "3x"
26 | },
27 | {
28 | "idiom" : "ipad",
29 | "scale" : "1x"
30 | },
31 | {
32 | "idiom" : "ipad",
33 | "scale" : "2x"
34 | },
35 | {
36 | "idiom" : "ipad",
37 | "scale" : "1x"
38 | },
39 | {
40 | "idiom" : "ipad",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x"
46 | },
47 | {
48 | "idiom" : "ipad",
49 | "scale" : "2x"
50 | }
51 | ],
52 | "info" : {
53 | "version" : 1,
54 | "author" : "xcode"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/fixtures/contents-no-sizes.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "filename" : "Icon-Small@2x.png",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "filename" : "Icon-Small@3x.png",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "ipad",
15 | "filename" : "Icon-40@2x.png",
16 | "scale" : "2x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/Bladefile:
--------------------------------------------------------------------------------
1 | blades:
2 | - source: source.png
3 | mount: after/Assets.xcassets/AppIcon.appiconset
4 | contents: true
5 | - source: source.png
6 | mount: after/Assets.xcassets/Watch.imageset
7 | contents: true
8 | - source: source.png
9 | mount: after/Assets.xcassets/Image.imageset
10 | contents: true
11 |
12 |
13 |
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "filename": "source-iphone-29@2x.png",
5 | "idiom": "iphone",
6 | "scale": "2x",
7 | "size": "29x29"
8 | },
9 | {
10 | "filename": "source-iphone-29@3x.png",
11 | "idiom": "iphone",
12 | "scale": "3x",
13 | "size": "29x29"
14 | },
15 | {
16 | "filename": "source-iphone-40@2x.png",
17 | "idiom": "iphone",
18 | "scale": "2x",
19 | "size": "40x40"
20 | },
21 | {
22 | "filename": "source-iphone-40@3x.png",
23 | "idiom": "iphone",
24 | "scale": "3x",
25 | "size": "40x40"
26 | },
27 | {
28 | "filename": "source-iphone-60@2x.png",
29 | "idiom": "iphone",
30 | "scale": "2x",
31 | "size": "60x60"
32 | },
33 | {
34 | "filename": "source-iphone-60@3x.png",
35 | "idiom": "iphone",
36 | "scale": "3x",
37 | "size": "60x60"
38 | }
39 | ],
40 | "info": {
41 | "author": "xcode",
42 | "version": 1
43 | }
44 | }
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-29@2x.png
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-29@3x.png
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-40@2x.png
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-40@3x.png
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-60@2x.png
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-60@3x.png
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/after/Assets.xcassets/Image.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "filename": "source-universal-333@1x.png",
5 | "idiom": "universal",
6 | "scale": "1x",
7 | "size": "333.3333333333333x333.3333333333333"
8 | },
9 | {
10 | "filename": "source-universal-333@2x.png",
11 | "idiom": "universal",
12 | "scale": "2x",
13 | "size": "333.3333333333333x333.3333333333333"
14 | },
15 | {
16 | "filename": "source-universal-333@3x.png",
17 | "idiom": "universal",
18 | "scale": "3x",
19 | "size": "333.3333333333333x333.3333333333333"
20 | }
21 | ],
22 | "info": {
23 | "author": "xcode",
24 | "version": 1
25 | }
26 | }
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/after/Assets.xcassets/Image.imageset/source-universal-333@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/Image.imageset/source-universal-333@1x.png
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/after/Assets.xcassets/Image.imageset/source-universal-333@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/Image.imageset/source-universal-333@2x.png
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/after/Assets.xcassets/Image.imageset/source-universal-333@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/Image.imageset/source-universal-333@3x.png
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "filename": "source-universal-333@1x.png",
5 | "idiom": "universal",
6 | "scale": "1x",
7 | "size": "333.3333333333333x333.3333333333333"
8 | },
9 | {
10 | "filename": "source-universal-333@2x.png",
11 | "idiom": "universal",
12 | "scale": "2x",
13 | "size": "333.3333333333333x333.3333333333333"
14 | },
15 | {
16 | "filename": "source-universal-333@3x.png",
17 | "idiom": "universal",
18 | "scale": "3x",
19 | "size": "333.3333333333333x333.3333333333333"
20 | },
21 | {
22 | "filename": "source-watch-500@2x.png",
23 | "idiom": "watch",
24 | "scale": "2x",
25 | "size": "500x500"
26 | },
27 | {
28 | "filename": "source-watch-500@2x.png",
29 | "idiom": "watch",
30 | "scale": "2x",
31 | "size": "500x500"
32 | },
33 | {
34 | "filename": "source-watch-500@2x.png",
35 | "idiom": "watch",
36 | "scale": "2x",
37 | "size": "500x500"
38 | }
39 | ],
40 | "info": {
41 | "author": "xcode",
42 | "version": 1
43 | }
44 | }
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/source-universal-333@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/source-universal-333@1x.png
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/source-universal-333@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/source-universal-333@2x.png
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/source-universal-333@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/source-universal-333@3x.png
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/source-watch-500@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/source-watch-500@2x.png
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/before/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/before/Assets.xcassets/Image.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "scale" : "3x"
14 | }
15 | ],
16 | "info" : {
17 | "version" : 1,
18 | "author" : "xcode"
19 | }
20 | }
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/before/Assets.xcassets/Watch.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "scale" : "3x"
14 | },
15 | {
16 | "idiom" : "watch",
17 | "scale" : "2x"
18 | },
19 | {
20 | "idiom" : "watch",
21 | "scale" : "2x",
22 | "screen-width" : "<=145"
23 | },
24 | {
25 | "idiom" : "watch",
26 | "scale" : "2x",
27 | "screen-width" : ">145"
28 | }
29 | ],
30 | "info" : {
31 | "version" : 1,
32 | "author" : "xcode"
33 | }
34 | }
--------------------------------------------------------------------------------
/fixtures/full_integration/interpolation/source.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/source.png
--------------------------------------------------------------------------------
/fixtures/single-file.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "filename" : "Icon-Small@2x.png",
6 | "scale" : "2x"
7 | }
8 | ],
9 | "info" : {
10 | "version" : 1,
11 | "author" : "xcode"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/fixtures/with-files.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "filename" : "Icon-Small@2x.png",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/integration.rb:
--------------------------------------------------------------------------------
1 | require 'fileutils'
2 | include FileUtils
3 |
4 | def error_if(condition, msg)
5 | return unless condition
6 | puts msg
7 | exit(1)
8 | end
9 |
10 | puts "Running integration tests..."
11 |
12 | blade = File.join(File.expand_path(File.dirname(__FILE__)), 'blade')
13 | tests = %w{ interpolation }
14 |
15 | tests.each do |test|
16 | test_dir = File.expand_path("fixtures/full_integration/#{test}", File.dirname(__FILE__))
17 | cd test_dir
18 | rm_rf "after"
19 | cp_r "before", "after"
20 |
21 | 2.times do
22 | blade_out = `#{blade}`
23 | error_if(!blade_out.empty?, blade_out)
24 | exit(1) unless blade_out.empty?
25 | end
26 |
27 | diff_out = `git diff #{test_dir}`
28 | error_if(!diff_out.empty?, diff_out)
29 | putc "."
30 | end
31 |
32 | puts "\nOK"
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | log "github.com/Sirupsen/logrus"
6 | "gopkg.in/alecthomas/kingpin.v2"
7 | "io/ioutil"
8 | "os"
9 | )
10 |
11 | var VERSION string
12 |
13 | var shouldInit = kingpin.Flag("init", "Initialize a Bladefile").Bool()
14 | var contentsTemplate = kingpin.Flag("template", "Contents json template to generate for.").Short('t').Default("Contents.json").String()
15 | var interpolation = kingpin.Flag("interpolation", "Interpolation: l2, l3 (Lanczos 2 and 3), n (nearest neighbor), bc (bicubic), bl (bilinear), mn (Mitchell-Netravali)").Short('i').Default("l3").String()
16 | var source = kingpin.Flag("source", "Source image to use. For optimal results supply a highest size PNG.").Short('s').String()
17 | var out = kingpin.Flag("out", "Out folder. Use current folder if none given.").Short('o').String()
18 | var includeContents = kingpin.Flag("catalog", "Include generation of a new image catalog Contents.json").Short('c').Bool()
19 | var dryRun = kingpin.Flag("dryrun", "Do a dry run, don't write out anything").Short('d').Bool()
20 | var mount = kingpin.Flag("mount", "Mount on an existing image catalog (take its Contents.json and output images to it)").Short('m').String()
21 | var verbose = kingpin.Flag("verbose", "Verbose output").Bool()
22 | var version = kingpin.Flag("version", "Current version").Short('v').Bool()
23 |
24 | var BLADEFILE = `#
25 | # Uncomment below to specify your own resources.
26 | # See https://github.com/jondot/blade for more information.
27 | #
28 |
29 | blades:
30 | - source: assets/icon-1024x1024.png # this image should be the only image, and the biggest image you can use (typically for icons, 1024x1024)
31 | mount: project_name/Assets.xcassets/AppIcon.appiconset
32 | contents: true # use 'false' if you want to only update existing catalog, 'true' if you want to generate a full catalog every time
33 | # - source: spaceship.png
34 | # mount: project_name/Images.xcassets/Spaceship.imageset # you can also generate regular image sets, not just app icons
35 | # - source: iTunesArtwork@2x.png
36 | # template: templates/iphone-appicon.json # use a template if you want to batch build catalogs regardless of an xcode project (e.g. as part of a CI process)
37 | # out: out/newiphone
38 | # contents: true
39 | `
40 |
41 | func init() {
42 | log.SetLevel(log.FatalLevel)
43 | }
44 | func main() {
45 | kingpin.Parse()
46 |
47 | if *version {
48 | println(VERSION)
49 | os.Exit(0)
50 | }
51 |
52 | if *shouldInit {
53 | ioutil.WriteFile("Bladefile", []byte(BLADEFILE), 0644)
54 | fmt.Println("Wrote Bladefile.\nYou have 1 blade set up with recommended settings, please review that it matches your project.")
55 | os.Exit(0)
56 | }
57 |
58 | if *verbose {
59 | log.SetLevel(log.InfoLevel)
60 | }
61 | bladefile := Bladefile{}
62 | if bladefile.Exists() {
63 | log.Infof("Found a local Bladefile.")
64 | err := bladefile.Load()
65 | if err != nil {
66 | log.Fatalf("Cannot load bladefile (%s)", err)
67 | }
68 |
69 | log.Infof("Bladefile contains %d blade defs.", len(bladefile.Blades))
70 | for _, blade := range bladefile.Blades {
71 | blade.Run()
72 | }
73 | } else {
74 | blade := Blade{
75 | Template: *contentsTemplate,
76 | Interpolation: *interpolation,
77 | Source: *source,
78 | Out: *out,
79 | IncludeContents: *includeContents,
80 | DryRun: *dryRun,
81 | Mount: *mount,
82 | }
83 | blade.Run()
84 |
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/resize_converter.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | log "github.com/Sirupsen/logrus"
5 | "github.com/nfnt/resize"
6 | "image"
7 | _ "image/jpeg"
8 | "image/png"
9 | "os"
10 | )
11 |
12 | var interpolations = map[string]resize.InterpolationFunction{
13 | "l3": resize.Lanczos3,
14 | "l2": resize.Lanczos2,
15 | "n": resize.NearestNeighbor,
16 | "bc": resize.Bicubic,
17 | "bl": resize.Bilinear,
18 | "mn": resize.MitchellNetravali,
19 | }
20 |
21 | type ResizeConverter struct {
22 | Interpolation string
23 | }
24 |
25 | func NewResizeConverter() *ResizeConverter {
26 | return &ResizeConverter{}
27 | }
28 |
29 | func (r *ResizeConverter) Size(inFile string) (Rect, error) {
30 | in, err := os.Open(inFile)
31 | if err != nil {
32 | log.Fatalf("Resize: cannot open file '%s' (%s).", inFile, err)
33 | }
34 |
35 | source, _, err := image.Decode(in)
36 | if err != nil {
37 | return Rect{}, err
38 | }
39 |
40 | return Rect{
41 | float64(source.Bounds().Dx()),
42 | float64(source.Bounds().Dy()),
43 | }, nil
44 | }
45 |
46 | func (r *ResizeConverter) Convert(inFile string, outFile string, rect Rect) error {
47 | in, err := os.Open(inFile)
48 | if err != nil {
49 | log.Fatalf("Runner(File): cannot open source file '%s' for reading (%s).", inFile, err)
50 | }
51 | defer in.Close()
52 |
53 | out, err := os.Create(outFile)
54 | if err != nil {
55 | log.Printf("ERROR: cannot create new file %s (%s)", outFile, err)
56 | }
57 | defer out.Close()
58 |
59 | source, _, err := image.Decode(in)
60 | if err != nil {
61 | return err
62 | }
63 |
64 | interp, ok := interpolations[r.Interpolation]
65 | if !ok {
66 | interp = interpolations["l3"]
67 | }
68 |
69 | resized := resize.Resize(uint(rect.Width), uint(rect.Height), source, interp)
70 | png.Encode(out, resized)
71 | return nil
72 | }
73 |
--------------------------------------------------------------------------------
/runner.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | log "github.com/Sirupsen/logrus"
6 |
7 | "path"
8 | "strings"
9 | )
10 |
11 | type Runner struct {
12 | converter Converter
13 | contents *Contents
14 | SourceFile string
15 | OutDir string
16 | GenerateContents bool
17 | }
18 |
19 | func NewRunner(contents *Contents, converter Converter, sourceFile string) *Runner {
20 | return &Runner{contents: contents, converter: converter, SourceFile: sourceFile}
21 | }
22 |
23 | func (f *Runner) run() {
24 | contents := f.contents
25 |
26 | sourceRect, err := f.converter.Size(f.SourceFile)
27 | if err != nil {
28 | log.Fatalf("Runner(File): cannot decode source file '%s' (%s).", f.SourceFile, err)
29 | }
30 |
31 | dim := NewDimensions()
32 | for i, _ := range contents.Images {
33 | meta := &contents.Images[i]
34 | rect := dim.Compute(contents, meta, sourceRect)
35 |
36 | // sync meta structure with the new data we've generated
37 | if meta.Size == "" {
38 | meta.Size = fmt.Sprintf("%vx%v", float64(rect.Width)/float64(meta.GetScale()), float64(rect.Height)/float64(meta.GetScale()))
39 | }
40 | if meta.Filename == "" {
41 | baseName := strings.TrimSuffix(path.Base(f.SourceFile), path.Ext(f.SourceFile))
42 | meta.Filename = meta.BuildFilename(baseName, rect)
43 | }
44 |
45 | outpath := path.Join(f.OutDir, meta.Filename)
46 | err = f.converter.Convert(f.SourceFile, outpath, rect)
47 | if err != nil {
48 | log.Printf("ERROR: cannot convert %s (%s)", outpath, err)
49 | }
50 |
51 | log.Infof("[%s] -> %v", f.OutDir, contents.Images[i])
52 | }
53 | log.Infof("[%s] %d images generated.", f.OutDir, len(contents.Images))
54 |
55 | if f.GenerateContents {
56 | err := contents.WriteToFile(path.Join(f.OutDir, "Contents.json"))
57 | if err != nil {
58 | log.Fatalf("Could not write Contents.json to %s (%s).", f.OutDir, err)
59 | }
60 | log.Infof("[%s] Wrote Contents.json.", f.OutDir)
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/runner_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/stretchr/testify/mock"
5 | . "gopkg.in/check.v1"
6 | )
7 |
8 | type MockConverter struct {
9 | mock.Mock
10 | }
11 |
12 | func (_m *MockConverter) Convert(_a0 string, _a1 string, _a2 Rect) error {
13 | ret := _m.Called(_a0, _a1, _a2)
14 |
15 | var r0 error
16 | if rf, ok := ret.Get(0).(func(string, string, Rect) error); ok {
17 | r0 = rf(_a0, _a1, _a2)
18 | } else {
19 | r0 = ret.Error(0)
20 | }
21 |
22 | return r0
23 | }
24 | func (_m *MockConverter) Size(_a0 string) (Rect, error) {
25 | ret := _m.Called(_a0)
26 |
27 | var r0 Rect
28 | if rf, ok := ret.Get(0).(func(string) Rect); ok {
29 | r0 = rf(_a0)
30 | } else {
31 | r0 = ret.Get(0).(Rect)
32 | }
33 |
34 | var r1 error
35 | if rf, ok := ret.Get(1).(func(string) error); ok {
36 | r1 = rf(_a0)
37 | } else {
38 | r1 = ret.Error(1)
39 | }
40 |
41 | return r0, r1
42 | }
43 |
44 | type RunnerSuite struct {
45 | conv Converter
46 | }
47 |
48 | var _ = Suite(&RunnerSuite{
49 | conv: &RunnerSuiteConverter{},
50 | })
51 |
52 | type RunnerSuiteConverter struct {
53 | }
54 |
55 | func (r *RunnerSuiteConverter) Convert(infile string, outfile string, sourceRect Rect) error {
56 | return nil
57 | }
58 |
59 | func (r *RunnerSuiteConverter) Size(infile string) (Rect, error) {
60 | return Rect{60, 60}, nil
61 | }
62 |
63 | func (s *RunnerSuite) Testshould_auto_complete_filename_given_missing_in_template(c *C) {
64 | contents := NewContentsFromFile("fixtures/contents-no-sizes-no-files.json")
65 | r := NewRunner(contents, s.conv, "sourcefile.png")
66 |
67 | for _, meta := range contents.Images {
68 | c.Check(meta.Filename, Equals, "")
69 | }
70 | r.run()
71 |
72 | for _, meta := range contents.Images {
73 | c.Check(meta.Filename, Not(Equals), "")
74 | }
75 | c.Check(contents.Images[0].Filename, Equals, "sourcefile-iphone-20@2x.png")
76 | c.Check(contents.Images[6].Filename, Equals, "sourcefile-ipad-30@1x.png")
77 | }
78 |
79 | func (s *RunnerSuite) Test_should_leave_file_as_is_given_in_template(c *C) {
80 | contents := NewContentsFromFile("fixtures/with-files.json")
81 | r := NewRunner(contents, s.conv, "sourcefile.png")
82 |
83 | r.run()
84 |
85 | for _, meta := range contents.Images {
86 | c.Check(meta.Filename, Not(Equals), "")
87 | }
88 | c.Check(contents.Images[0].Filename, Equals, "Icon-Small@2x.png")
89 | c.Check(contents.Images[1].Filename, Equals, "sourcefile-iphone-20@3x.png")
90 | }
91 |
92 | func (s *RunnerSuite) Test_should_use_outdir_given_nonempty(c *C) {
93 | contents := NewContentsFromFile("fixtures/single-file.json")
94 | mk := &MockConverter{}
95 | mk.On("Size", "sourcefile.png").Return(Rect{42, 42}, nil)
96 | // 28 because it's 2/3
97 | mk.On("Convert", "sourcefile.png", "mydir/Icon-Small@2x.png", Rect{42, 42}).Return(nil)
98 |
99 | r := NewRunner(contents, mk, "sourcefile.png")
100 | r.OutDir = "mydir"
101 |
102 | r.run()
103 |
104 | mk.AssertExpectations(c)
105 | }
106 |
107 | func (s *RunnerSuite) Test_should_fill_in_computed_size_given_empty_size(c *C) {
108 | contents := NewContentsFromFile("fixtures/contents-no-sizes.json")
109 | r := NewRunner(contents, s.conv, "sourcefile.png")
110 |
111 | r.run()
112 |
113 | c.Check(contents.Images[0].Size, Equals, "20x20")
114 | }
115 |
116 | func (s *RunnerSuite) Test_should_convert_given_list_of_images(c *C) {
117 | contents := NewContentsFromFile("fixtures/contents-appicon.json")
118 | mk := &MockConverter{}
119 | mk.On("Size", "sourcefile.png").Return(Rect{42, 42}, nil)
120 | // 28 because it's 2/3
121 | mk.On("Convert", mock.Anything, mock.Anything, mock.Anything).Return(nil).Times(12)
122 |
123 | r := NewRunner(contents, mk, "sourcefile.png")
124 | r.OutDir = "mydir"
125 |
126 | r.run()
127 |
128 | mk.AssertExpectations(c)
129 | }
130 |
--------------------------------------------------------------------------------
/templates/iphone-appicon.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "29x29",
5 | "idiom" : "iphone",
6 | "scale" : "2x"
7 | },
8 | {
9 | "size" : "29x29",
10 | "idiom" : "iphone",
11 | "scale" : "3x"
12 | },
13 | {
14 | "size" : "40x40",
15 | "idiom" : "iphone",
16 | "scale" : "2x"
17 | },
18 | {
19 | "size" : "40x40",
20 | "idiom" : "iphone",
21 | "scale" : "3x"
22 | },
23 | {
24 | "size" : "60x60",
25 | "idiom" : "iphone",
26 | "scale" : "2x"
27 | },
28 | {
29 | "size" : "60x60",
30 | "idiom" : "iphone",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "29x29",
35 | "idiom" : "ipad",
36 | "scale" : "1x"
37 | },
38 | {
39 | "size" : "29x29",
40 | "idiom" : "ipad",
41 | "scale" : "2x"
42 | },
43 | {
44 | "size" : "40x40",
45 | "idiom" : "ipad",
46 | "scale" : "1x"
47 | },
48 | {
49 | "size" : "40x40",
50 | "idiom" : "ipad",
51 | "scale" : "2x"
52 | },
53 | {
54 | "size" : "76x76",
55 | "idiom" : "ipad",
56 | "scale" : "1x"
57 | },
58 | {
59 | "size" : "76x76",
60 | "idiom" : "ipad",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/templates/universal.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "minus-86@2x.png"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/templates/watch.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "watch",
5 | "scale" : "2x"
6 | },
7 | {
8 | "idiom" : "watch",
9 | "screenWidth" : "{130,145}",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "watch",
14 | "screenWidth" : "{146,165}",
15 | "scale" : "2x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/test_helper_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | . "gopkg.in/check.v1"
5 | "testing"
6 | )
7 |
8 | func Test(t *testing.T) { TestingT(t) }
9 |
--------------------------------------------------------------------------------