Update godeps to include go-semi-semantic

[finishes #107756410]
This commit is contained in:
Evan Short
2015-11-10 14:21:54 -08:00
parent 8ec667151f
commit 8291efc7a7
54 changed files with 1655 additions and 929 deletions

22
Godeps/Godeps.json generated
View File

@@ -1,33 +1,35 @@
{ {
"ImportPath": "github.com/concourse/github-release-resource", "ImportPath": "github.com/concourse/github-release-resource",
"GoVersion": "go1.5.1", "GoVersion": "go1.5.1",
"Packages": [
"./..."
],
"Deps": [ "Deps": [
{ {
"ImportPath": "code.google.com/p/goauth2/oauth", "ImportPath": "code.google.com/p/goauth2/oauth",
"Comment": "weekly-56", "Comment": "weekly-57",
"Rev": "afe77d958c701557ec5dc56f6936fcc194d15520" "Rev": "'222e66a2882ef45c3153c86c0aa82fe65fc29a78'"
}, },
{ {
"ImportPath": "github.com/blang/semver", "ImportPath": "github.com/cppforlife/go-semi-semantic/version",
"Comment": "v3.0.0", "Rev": "a7ba07c61e58e6df7693d49a13d24f533182518a"
"Rev": "2f3112b6f8f18f9df8743cc75ed51da08094ef6a"
}, },
{ {
"ImportPath": "github.com/golang/protobuf/proto", "ImportPath": "github.com/golang/protobuf/proto",
"Rev": "9ebc6c4ed925b1323a0b11cea09e527cdc4e557c" "Rev": "d3d78384b82d449651d2435ed329d70f7c48aa56"
}, },
{ {
"ImportPath": "github.com/google/go-querystring/query", "ImportPath": "github.com/google/go-querystring/query",
"Rev": "d8840cbb2baa915f4836edda4750050a2c0b7aea" "Rev": "2a60fc2ba6c19de80291203597d752e9ba58e4c0"
}, },
{ {
"ImportPath": "github.com/mitchellh/colorstring", "ImportPath": "github.com/mitchellh/colorstring",
"Rev": "15fc698eaae194ff160846daf77922a45b9290a7" "Rev": "8631ce90f28644f54aeedcb3e389a85174e067d1"
}, },
{ {
"ImportPath": "github.com/onsi/ginkgo", "ImportPath": "github.com/onsi/ginkgo",
"Comment": "v1.1.0-38-g5ed93e4", "Comment": "v1.2.0-22-g39d2c24",
"Rev": "5ed93e443a4b7dfe9f5e95ca87e6082e503021d2" "Rev": "39d2c24f8a92c88f7e7f4d8ec6c80d3cc8f5ac65"
}, },
{ {
"ImportPath": "github.com/onsi/gomega", "ImportPath": "github.com/onsi/gomega",

View File

@@ -0,0 +1,27 @@
Copyright (c) 2009 The goauth2 Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the goauth2 project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

View File

@@ -4,6 +4,10 @@
// Package oauth supports making OAuth2-authenticated HTTP requests. // Package oauth supports making OAuth2-authenticated HTTP requests.
// //
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!! DEPRECATED. Use golang.org/x/oauth2 instead. !!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// Example usage: // Example usage:
// //
// // Specify your configuration. (typically as a global variable) // // Specify your configuration. (typically as a global variable)

View File

@@ -1,22 +0,0 @@
The MIT License
Copyright (c) 2014 Benedikt Lang <github at benediktlang.de>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,142 +0,0 @@
semver for golang [![Build Status](https://drone.io/github.com/blang/semver/status.png)](https://drone.io/github.com/blang/semver/latest) [![GoDoc](https://godoc.org/github.com/blang/semver?status.png)](https://godoc.org/github.com/blang/semver) [![Coverage Status](https://img.shields.io/coveralls/blang/semver.svg)](https://coveralls.io/r/blang/semver?branch=master)
======
semver is a [Semantic Versioning](http://semver.org/) library written in golang. It fully covers spec version `2.0.0`.
Usage
-----
```bash
$ go get github.com/blang/semver
```
Note: Always vendor your dependencies or fix on a specific version tag.
```go
import github.com/blang/semver
v1, err := semver.Make("1.0.0-beta")
v2, err := semver.Make("2.0.0-beta")
v1.Compare(v2)
```
Also check the [GoDocs](http://godoc.org/github.com/blang/semver).
Why should I use this lib?
-----
- Fully spec compatible
- No reflection
- No regex
- Fully tested (Coverage >99%)
- Readable parsing/validation errors
- Fast (See [Benchmarks](#benchmarks))
- Only Stdlib
- Uses values instead of pointers
- Many features, see below
Features
-----
- Parsing and validation at all levels
- Comparator-like comparisons
- Compare Helper Methods
- InPlace manipulation
- Sortable (implements sort.Interface)
- database/sql compatible (sql.Scanner/Valuer)
- encoding/json compatible (json.Marshaler/Unmarshaler)
Example
-----
Have a look at full examples in [examples/main.go](examples/main.go)
```go
import github.com/blang/semver
v, err := semver.Make("0.0.1-alpha.preview+123.github")
fmt.Printf("Major: %d\n", v.Major)
fmt.Printf("Minor: %d\n", v.Minor)
fmt.Printf("Patch: %d\n", v.Patch)
fmt.Printf("Pre: %s\n", v.Pre)
fmt.Printf("Build: %s\n", v.Build)
// Prerelease versions array
if len(v.Pre) > 0 {
fmt.Println("Prerelease versions:")
for i, pre := range v.Pre {
fmt.Printf("%d: %q\n", i, pre)
}
}
// Build meta data array
if len(v.Build) > 0 {
fmt.Println("Build meta data:")
for i, build := range v.Build {
fmt.Printf("%d: %q\n", i, build)
}
}
v001, err := semver.Make("0.0.1")
// Compare using helpers: v.GT(v2), v.LT, v.GTE, v.LTE
v001.GT(v) == true
v.LT(v001) == true
v.GTE(v) == true
v.LTE(v) == true
// Or use v.Compare(v2) for comparisons (-1, 0, 1):
v001.Compare(v) == 1
v.Compare(v001) == -1
v.Compare(v) == 0
// Manipulate Version in place:
v.Pre[0], err = semver.NewPRVersion("beta")
if err != nil {
fmt.Printf("Error parsing pre release version: %q", err)
}
fmt.Println("\nValidate versions:")
v.Build[0] = "?"
err = v.Validate()
if err != nil {
fmt.Printf("Validation failed: %s\n", err)
}
```
Benchmarks
-----
BenchmarkParseSimple 5000000 328 ns/op 49 B/op 1 allocs/op
BenchmarkParseComplex 1000000 2105 ns/op 263 B/op 7 allocs/op
BenchmarkParseAverage 1000000 1301 ns/op 168 B/op 4 allocs/op
BenchmarkStringSimple 10000000 130 ns/op 5 B/op 1 allocs/op
BenchmarkStringLarger 5000000 280 ns/op 32 B/op 2 allocs/op
BenchmarkStringComplex 3000000 512 ns/op 80 B/op 3 allocs/op
BenchmarkStringAverage 5000000 387 ns/op 47 B/op 2 allocs/op
BenchmarkValidateSimple 500000000 7.92 ns/op 0 B/op 0 allocs/op
BenchmarkValidateComplex 2000000 923 ns/op 0 B/op 0 allocs/op
BenchmarkValidateAverage 5000000 452 ns/op 0 B/op 0 allocs/op
BenchmarkCompareSimple 100000000 11.2 ns/op 0 B/op 0 allocs/op
BenchmarkCompareComplex 50000000 40.9 ns/op 0 B/op 0 allocs/op
BenchmarkCompareAverage 50000000 43.8 ns/op 0 B/op 0 allocs/op
BenchmarkSort 5000000 436 ns/op 259 B/op 2 allocs/op
See benchmark cases at [semver_test.go](semver_test.go)
Motivation
-----
I simply couldn't find any lib supporting the full spec. Others were just wrong or used reflection and regex which i don't like.
Contribution
-----
Feel free to make a pull request. For bigger changes create a issue first to discuss about it.
License
-----
See [LICENSE](LICENSE) file.

View File

@@ -1,83 +0,0 @@
package main
import (
"fmt"
"github.com/blang/semver"
)
func main() {
v, err := semver.Parse("0.0.1-alpha.preview.222+123.github")
if err != nil {
fmt.Printf("Error while parsing (not valid): %q", err)
}
fmt.Printf("Version to string: %q\n", v)
fmt.Printf("Major: %d\n", v.Major)
fmt.Printf("Minor: %d\n", v.Minor)
fmt.Printf("Patch: %d\n", v.Patch)
// Prerelease versions
if len(v.Pre) > 0 {
fmt.Println("Prerelease versions:")
for i, pre := range v.Pre {
fmt.Printf("%d: %q\n", i, pre)
}
}
// Build meta data
if len(v.Build) > 0 {
fmt.Println("Build meta data:")
for i, build := range v.Build {
fmt.Printf("%d: %q\n", i, build)
}
}
// Make == Parse (Value), New for Pointer
v001, err := semver.Make("0.0.1")
fmt.Println("\nUse Version.Compare for comparisons (-1, 0, 1):")
fmt.Printf("%q is greater than %q: Compare == %d\n", v001, v, v001.Compare(v))
fmt.Printf("%q is less than %q: Compare == %d\n", v, v001, v.Compare(v001))
fmt.Printf("%q is equal to %q: Compare == %d\n", v, v, v.Compare(v))
fmt.Println("\nUse comparison helpers returning booleans:")
fmt.Printf("%q is greater than %q: %t\n", v001, v, v001.GT(v))
fmt.Printf("%q is greater than equal %q: %t\n", v001, v, v001.GTE(v))
fmt.Printf("%q is greater than equal %q: %t\n", v, v, v.GTE(v))
fmt.Printf("%q is less than %q: %t\n", v, v001, v.LT(v001))
fmt.Printf("%q is less than equal %q: %t\n", v, v001, v.LTE(v001))
fmt.Printf("%q is less than equal %q: %t\n", v, v, v.LTE(v))
fmt.Println("\nManipulate Version in place:")
v.Pre[0], err = semver.NewPRVersion("beta")
if err != nil {
fmt.Printf("Error parsing pre release version: %q", err)
}
fmt.Printf("Version to string: %q\n", v)
fmt.Println("\nCompare Prerelease versions:")
pre1, _ := semver.NewPRVersion("123")
pre2, _ := semver.NewPRVersion("alpha")
pre3, _ := semver.NewPRVersion("124")
fmt.Printf("%q is less than %q: Compare == %d\n", pre1, pre2, pre1.Compare(pre2))
fmt.Printf("%q is greater than %q: Compare == %d\n", pre3, pre1, pre3.Compare(pre1))
fmt.Printf("%q is equal to %q: Compare == %d\n", pre1, pre1, pre1.Compare(pre1))
fmt.Println("\nValidate versions:")
v.Build[0] = "?"
err = v.Validate()
if err != nil {
fmt.Printf("Validation failed: %s\n", err)
}
fmt.Println("Create valid build meta data:")
b1, _ := semver.NewBuildVersion("build123")
v.Build[0] = b1
fmt.Printf("Version with new build version %q\n", v)
_, err = semver.NewBuildVersion("build?123")
if err != nil {
fmt.Printf("Create build version failed: %s\n", err)
}
}

View File

@@ -1,23 +0,0 @@
package semver
import (
"encoding/json"
)
// MarshalJSON implements the encoding/json.Marshaler interface.
func (v Version) MarshalJSON() ([]byte, error) {
return json.Marshal(v.String())
}
// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
func (v *Version) UnmarshalJSON(data []byte) (err error) {
var versionString string
if err = json.Unmarshal(data, &versionString); err != nil {
return
}
*v, err = Parse(versionString)
return
}

View File

@@ -1,398 +0,0 @@
package semver
import (
"errors"
"fmt"
"strconv"
"strings"
)
const (
numbers string = "0123456789"
alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-"
alphanum = alphas + numbers
)
// Latest fully supported spec version
var SPEC_VERSION = Version{
Major: 2,
Minor: 0,
Patch: 0,
}
type Version struct {
Major uint64
Minor uint64
Patch uint64
Pre []PRVersion
Build []string //No Precendence
}
// Version to string
func (v Version) String() string {
b := make([]byte, 0, 5)
b = strconv.AppendUint(b, v.Major, 10)
b = append(b, '.')
b = strconv.AppendUint(b, v.Minor, 10)
b = append(b, '.')
b = strconv.AppendUint(b, v.Patch, 10)
if len(v.Pre) > 0 {
b = append(b, '-')
b = append(b, v.Pre[0].String()...)
for _, pre := range v.Pre[1:] {
b = append(b, '.')
b = append(b, pre.String()...)
}
}
if len(v.Build) > 0 {
b = append(b, '+')
b = append(b, v.Build[0]...)
for _, build := range v.Build[1:] {
b = append(b, '.')
b = append(b, build...)
}
}
return string(b)
}
// Checks if v is equal to o.
func (v Version) Equals(o Version) bool {
return (v.Compare(o) == 0)
}
// Checks if v is equal to o.
func (v Version) EQ(o Version) bool {
return (v.Compare(o) == 0)
}
// Checks if v is not equal to o.
func (v Version) NE(o Version) bool {
return (v.Compare(o) != 0)
}
// Checks if v is greater than o.
func (v Version) GT(o Version) bool {
return (v.Compare(o) == 1)
}
// Checks if v is greater than or equal to o.
func (v Version) GTE(o Version) bool {
return (v.Compare(o) >= 0)
}
// Checks if v is greater than or equal to o.
func (v Version) GE(o Version) bool {
return (v.Compare(o) >= 0)
}
// Checks if v is less than o.
func (v Version) LT(o Version) bool {
return (v.Compare(o) == -1)
}
// Checks if v is less than or equal to o.
func (v Version) LTE(o Version) bool {
return (v.Compare(o) <= 0)
}
// Checks if v is less than or equal to o.
func (v Version) LE(o Version) bool {
return (v.Compare(o) <= 0)
}
// Compares Versions v to o:
// -1 == v is less than o
// 0 == v is equal to o
// 1 == v is greater than o
func (v Version) Compare(o Version) int {
if v.Major != o.Major {
if v.Major > o.Major {
return 1
} else {
return -1
}
}
if v.Minor != o.Minor {
if v.Minor > o.Minor {
return 1
} else {
return -1
}
}
if v.Patch != o.Patch {
if v.Patch > o.Patch {
return 1
} else {
return -1
}
}
// Quick comparison if a version has no prerelease versions
if len(v.Pre) == 0 && len(o.Pre) == 0 {
return 0
} else if len(v.Pre) == 0 && len(o.Pre) > 0 {
return 1
} else if len(v.Pre) > 0 && len(o.Pre) == 0 {
return -1
} else {
i := 0
for ; i < len(v.Pre) && i < len(o.Pre); i++ {
if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 {
continue
} else if comp == 1 {
return 1
} else {
return -1
}
}
// If all pr versions are the equal but one has further prversion, this one greater
if i == len(v.Pre) && i == len(o.Pre) {
return 0
} else if i == len(v.Pre) && i < len(o.Pre) {
return -1
} else {
return 1
}
}
}
// Validates v and returns error in case
func (v Version) Validate() error {
// Major, Minor, Patch already validated using uint64
for _, pre := range v.Pre {
if !pre.IsNum { //Numeric prerelease versions already uint64
if len(pre.VersionStr) == 0 {
return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr)
}
if !containsOnly(pre.VersionStr, alphanum) {
return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr)
}
}
}
for _, build := range v.Build {
if len(build) == 0 {
return fmt.Errorf("Build meta data can not be empty %q", build)
}
if !containsOnly(build, alphanum) {
return fmt.Errorf("Invalid character(s) found in build meta data %q", build)
}
}
return nil
}
// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error
func New(s string) (vp *Version, err error) {
v, err := Parse(s)
vp = &v
return
}
// Make is an alias for Parse, parses version string and returns a validated Version or error
func Make(s string) (Version, error) {
return Parse(s)
}
// Parse parses version string and returns a validated Version or error
func Parse(s string) (Version, error) {
if len(s) == 0 {
return Version{}, errors.New("Version string empty")
}
// Split into major.minor.(patch+pr+meta)
parts := strings.SplitN(s, ".", 3)
if len(parts) != 3 {
return Version{}, errors.New("No Major.Minor.Patch elements found")
}
// Major
if !containsOnly(parts[0], numbers) {
return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0])
}
if hasLeadingZeroes(parts[0]) {
return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0])
}
major, err := strconv.ParseUint(parts[0], 10, 64)
if err != nil {
return Version{}, err
}
// Minor
if !containsOnly(parts[1], numbers) {
return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1])
}
if hasLeadingZeroes(parts[1]) {
return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1])
}
minor, err := strconv.ParseUint(parts[1], 10, 64)
if err != nil {
return Version{}, err
}
v := Version{}
v.Major = major
v.Minor = minor
var build, prerelease []string
patchStr := parts[2]
if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 {
build = strings.Split(patchStr[buildIndex+1:], ".")
patchStr = patchStr[:buildIndex]
}
if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 {
prerelease = strings.Split(patchStr[preIndex+1:], ".")
patchStr = patchStr[:preIndex]
}
if !containsOnly(patchStr, numbers) {
return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr)
}
if hasLeadingZeroes(patchStr) {
return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr)
}
patch, err := strconv.ParseUint(patchStr, 10, 64)
if err != nil {
return Version{}, err
}
v.Patch = patch
// Prerelease
for _, prstr := range prerelease {
parsedPR, err := NewPRVersion(prstr)
if err != nil {
return Version{}, err
}
v.Pre = append(v.Pre, parsedPR)
}
// Build meta data
for _, str := range build {
if len(str) == 0 {
return Version{}, errors.New("Build meta data is empty")
}
if !containsOnly(str, alphanum) {
return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str)
}
v.Build = append(v.Build, str)
}
return v, nil
}
// MustParse is like Parse but panics if the version cannot be parsed.
func MustParse(s string) Version {
v, err := Parse(s)
if err != nil {
panic(`semver: Parse(` + s + `): ` + err.Error())
}
return v
}
// PreRelease Version
type PRVersion struct {
VersionStr string
VersionNum uint64
IsNum bool
}
// Creates a new valid prerelease version
func NewPRVersion(s string) (PRVersion, error) {
if len(s) == 0 {
return PRVersion{}, errors.New("Prerelease is empty")
}
v := PRVersion{}
if containsOnly(s, numbers) {
if hasLeadingZeroes(s) {
return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s)
}
num, err := strconv.ParseUint(s, 10, 64)
// Might never be hit, but just in case
if err != nil {
return PRVersion{}, err
}
v.VersionNum = num
v.IsNum = true
} else if containsOnly(s, alphanum) {
v.VersionStr = s
v.IsNum = false
} else {
return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s)
}
return v, nil
}
// Is pre release version numeric?
func (v PRVersion) IsNumeric() bool {
return v.IsNum
}
// Compares PreRelease Versions v to o:
// -1 == v is less than o
// 0 == v is equal to o
// 1 == v is greater than o
func (v PRVersion) Compare(o PRVersion) int {
if v.IsNum && !o.IsNum {
return -1
} else if !v.IsNum && o.IsNum {
return 1
} else if v.IsNum && o.IsNum {
if v.VersionNum == o.VersionNum {
return 0
} else if v.VersionNum > o.VersionNum {
return 1
} else {
return -1
}
} else { // both are Alphas
if v.VersionStr == o.VersionStr {
return 0
} else if v.VersionStr > o.VersionStr {
return 1
} else {
return -1
}
}
}
// PreRelease version to string
func (v PRVersion) String() string {
if v.IsNum {
return strconv.FormatUint(v.VersionNum, 10)
}
return v.VersionStr
}
func containsOnly(s string, set string) bool {
return strings.IndexFunc(s, func(r rune) bool {
return !strings.ContainsRune(set, r)
}) == -1
}
func hasLeadingZeroes(s string) bool {
return len(s) > 1 && s[0] == '0'
}
// Creates a new valid build version
func NewBuildVersion(s string) (string, error) {
if len(s) == 0 {
return "", errors.New("Buildversion is empty")
}
if !containsOnly(s, alphanum) {
return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s)
}
return s, nil
}

View File

@@ -1,24 +0,0 @@
package semver
import (
"sort"
)
type Versions []Version
func (s Versions) Len() int {
return len(s)
}
func (s Versions) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s Versions) Less(i, j int) bool {
return s[i].LT(s[j])
}
// Sort sorts a slice of versions
func Sort(versions []Version) {
sort.Sort(Versions(versions))
}

View File

@@ -1,30 +0,0 @@
package semver
import (
"database/sql/driver"
"fmt"
)
// Scan implements the database/sql.Scanner interface.
func (v *Version) Scan(src interface{}) (err error) {
var str string
switch src := src.(type) {
case string:
str = src
case []byte:
str = string(src)
default:
return fmt.Errorf("Version.Scan: cannot convert %T to string.", src)
}
if t, err := Parse(str); err == nil {
*v = t
}
return
}
// Value implements the database/sql/driver.Valuer interface.
func (s Version) Value() (driver.Value, error) {
return s.String(), nil
}

View File

@@ -0,0 +1,20 @@
Copyright (c) 2014 Dmitriy Kalinin
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,49 @@
package version
import (
"errors"
"regexp"
"strconv"
)
var (
verSegCompIntRegexp = regexp.MustCompile(`\A[0-9]+\z`)
)
type VerSegCompInt struct{ I int }
func NewVerSegCompIntFromString(piece string) (VerSegCompInt, bool, error) {
if !verSegCompIntRegexp.MatchString(piece) {
return VerSegCompInt{}, false, nil
}
i, err := strconv.Atoi(piece)
if err != nil {
return VerSegCompInt{}, true, err
}
return VerSegCompInt{i}, true, nil
}
func (i VerSegCompInt) Validate() error {
if i.I < 0 {
return errors.New("Expected integer component to be greater than or equal to 0")
}
return nil
}
func (i VerSegCompInt) Compare(other VerSegComp) int {
otherTyped := other.(VerSegCompInt)
switch {
case i.I < otherTyped.I:
return -1
case i.I == otherTyped.I:
return 0
case i.I > otherTyped.I:
return 1
}
panic("unreachable")
}
func (i VerSegCompInt) AsString() string { return strconv.Itoa(i.I) }

View File

@@ -0,0 +1,43 @@
package version
import (
"errors"
"regexp"
)
var (
verSegCompStrRegexp = regexp.MustCompile(`\A[0-9A-Za-z_\-]+\z`)
)
type VerSegCompStr struct{ S string }
func NewVerSegCompStrFromString(piece string) (VerSegCompStr, bool) {
if !verSegCompStrRegexp.MatchString(piece) {
return VerSegCompStr{}, false
}
return VerSegCompStr{piece}, true
}
func (s VerSegCompStr) Validate() error {
if len(s.S) == 0 {
return errors.New("Expected string component to be non-empty")
}
return nil
}
func (s VerSegCompStr) Compare(other VerSegComp) int {
otherTyped := other.(VerSegCompStr)
switch {
case s.S < otherTyped.S:
return -1
case s.S == otherTyped.S:
return 0
case s.S > otherTyped.S:
return 1
}
panic("unreachable")
}
func (s VerSegCompStr) AsString() string { return s.S }

View File

@@ -0,0 +1,152 @@
package version
import (
"errors"
"fmt"
"regexp"
)
var (
versionRegexp = matchingRegexp{regexp.MustCompile(`\A(?P<release>[0-9A-Za-z_\.]+)(\-(?P<pre_release>[0-9A-Za-z_\-\.]+))?(\+(?P<post_release>[0-9A-Za-z_\-\.]+))?\z`)}
)
type matchingRegexp struct {
*regexp.Regexp
}
type Version struct {
Release, PreRelease, PostRelease VersionSegment
Segments []VersionSegment
}
func NewVersionFromString(v string) (Version, error) {
var err error
if len(v) == 0 {
return Version{}, errors.New("Expected version to be non-empty string")
}
captures := versionRegexp.FindStringSubmatchMap(v)
if len(captures) == 0 {
errMsg := fmt.Sprintf("Expected version '%s' to match version format", v)
return Version{}, errors.New(errMsg)
}
release := VersionSegment{}
preRelease := VersionSegment{}
postRelease := VersionSegment{}
if releaseStr, ok := captures["release"]; ok {
release, err = NewVersionSegmentFromString(releaseStr)
if err != nil {
return Version{}, err
}
}
if preReleaseStr, ok := captures["pre_release"]; ok {
preRelease, err = NewVersionSegmentFromString(preReleaseStr)
if err != nil {
return Version{}, err
}
}
if postReleaseStr, ok := captures["post_release"]; ok {
postRelease, err = NewVersionSegmentFromString(postReleaseStr)
if err != nil {
return Version{}, err
}
}
return NewVersion(release, preRelease, postRelease)
}
func NewVersion(release, preRelease, postRelease VersionSegment) (Version, error) {
if release.Empty() {
return Version{}, errors.New("Expected to non-empty release segment for constructing version")
}
version := Version{
Release: release,
PreRelease: preRelease,
PostRelease: postRelease,
Segments: []VersionSegment{release, preRelease, postRelease},
}
return version, nil
}
func (v Version) String() string { return v.AsString() }
func (v Version) AsString() string {
result := v.Release.AsString()
if !v.PreRelease.Empty() {
result += "-" + v.PreRelease.AsString()
}
if !v.PostRelease.Empty() {
result += "+" + v.PostRelease.AsString()
}
return result
}
func (v Version) Compare(other Version) int {
result := v.Release.Compare(other.Release)
if result != 0 {
return result
}
if !v.PreRelease.Empty() || !other.PreRelease.Empty() {
if v.PreRelease.Empty() {
return 1
}
if other.PreRelease.Empty() {
return -1
}
result = v.PreRelease.Compare(other.PreRelease)
if result != 0 {
return result
}
}
if !v.PostRelease.Empty() || !other.PostRelease.Empty() {
if v.PostRelease.Empty() {
return -1
}
if other.PostRelease.Empty() {
return 1
}
result = v.PostRelease.Compare(other.PostRelease)
if result != 0 {
return result
}
}
return 0
}
func (v Version) IsEq(other Version) bool { return v.Compare(other) == 0 }
func (v Version) IsGt(other Version) bool { return v.Compare(other) == 1 }
func (v Version) IsLt(other Version) bool { return v.Compare(other) == -1 }
func (r *matchingRegexp) FindStringSubmatchMap(s string) map[string]string {
captures := map[string]string{}
match := r.FindStringSubmatch(s)
if match == nil {
return captures
}
for i, name := range r.SubexpNames() {
// 0 is a whole regex
if i == 0 || name == "" || match[i] == "" {
continue
}
captures[name] = match[i]
}
return captures
}

View File

@@ -0,0 +1,143 @@
package version
import (
"errors"
"fmt"
"strings"
)
type VerSegComp interface {
Validate() error
// Compare should panic if incompatible interface is given
Compare(VerSegComp) int
AsString() string
}
type VersionSegment struct {
Components []VerSegComp
}
func NewVersionSegmentFromString(v string) (VersionSegment, error) {
pieces := strings.Split(v, ".")
components := []VerSegComp{}
for _, p := range pieces {
i, matchedI, err := NewVerSegCompIntFromString(p)
if err != nil {
errMsg := fmt.Sprintf("Expected component '%s' from version segment '%s' to be a parseable integer: %s", p, v, err)
return VersionSegment{}, errors.New(errMsg)
}
if matchedI {
components = append(components, i)
} else if s, matched := NewVerSegCompStrFromString(p); matched {
components = append(components, s)
} else {
errMsg := fmt.Sprintf("Expected component '%s' from version segment '%s' to be either an integer or a formatted string", p, v)
return VersionSegment{}, errors.New(errMsg)
}
}
return VersionSegment{components}, nil
}
func NewVersionSegment(components []VerSegComp) (VersionSegment, error) {
if len(components) == 0 {
return VersionSegment{}, errors.New("Expected version segment to be build from at least one component")
}
for _, c := range components {
err := c.Validate()
if err != nil {
return VersionSegment{}, err
}
}
return VersionSegment{components}, nil
}
func (s VersionSegment) Empty() bool { return len(s.Components) == 0 }
func (s VersionSegment) AsString() string {
result := ""
for i, c := range s.Components {
result += c.AsString()
if i < len(s.Components)-1 {
result += "."
}
}
return result
}
func (s VersionSegment) Compare(other VersionSegment) int {
a := s.Components
b := other.Components
if len(a) > len(b) {
comparison := s.compareArrays(a[0:len(b)], b)
if comparison != 0 {
return comparison
}
if !s.isAllZeros(a[len(b):len(a)]) {
return 1
}
return 0
}
if len(a) < len(b) {
comparison := s.compareArrays(a, b[0:len(a)])
if comparison != 0 {
return comparison
}
if !s.isAllZeros(b[len(a):len(b)]) {
return -1
}
return 0
}
return s.compareArrays(a, b)
}
func (s VersionSegment) IsEq(other VersionSegment) bool { return s.Compare(other) == 0 }
func (s VersionSegment) IsGt(other VersionSegment) bool { return s.Compare(other) == 1 }
func (s VersionSegment) IsLt(other VersionSegment) bool { return s.Compare(other) == -1 }
// compareArrays compares 2 equally sized a & b
func (s VersionSegment) compareArrays(a, b []VerSegComp) int {
for i, v1 := range a {
v2 := b[i]
_, v1IsStr := v1.(VerSegCompStr)
_, v1IsInt := v1.(VerSegCompInt)
_, v2IsStr := v2.(VerSegCompStr)
_, v2IsInt := v2.(VerSegCompInt)
if v1IsStr && v2IsInt {
return 1
} else if v1IsInt && v2IsStr {
return -1
}
comparison := v1.Compare(v2)
if comparison != 0 {
return comparison
}
}
return 0
}
func (s VersionSegment) isAllZeros(a []VerSegComp) bool {
for _, v := range a {
vTyped, ok := v.(VerSegCompInt)
if !ok || vTyped.I != 0 {
return false
}
}
return true
}

View File

@@ -0,0 +1,31 @@
Go support for Protocol Buffers - Google's data interchange format
Copyright 2010 The Go Authors. All rights reserved.
https://github.com/golang/protobuf
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1115,9 +1115,8 @@ func (o *Buffer) enc_new_map(p *Properties, base structPointer) error {
return nil return nil
} }
keys := v.MapKeys() // Don't sort map keys. It is not required by the spec, and C++ doesn't do it.
sort.Sort(mapKeys(keys)) for _, key := range v.MapKeys() {
for _, key := range keys {
val := v.MapIndex(key) val := v.MapIndex(key)
// The only illegal map entry values are nil message pointers. // The only illegal map entry values are nil message pointers.

View File

@@ -216,7 +216,7 @@ The resulting file, test.pb.go, is:
To create and play with a Test object: To create and play with a Test object:
package main package main
import ( import (
"log" "log"

View File

@@ -37,6 +37,7 @@ package proto
import ( import (
"fmt" "fmt"
"log"
"os" "os"
"reflect" "reflect"
"sort" "sort"
@@ -809,3 +810,29 @@ func RegisterEnum(typeName string, unusedNameMap map[int32]string, valueMap map[
func EnumValueMap(enumType string) map[string]int32 { func EnumValueMap(enumType string) map[string]int32 {
return enumValueMaps[enumType] return enumValueMaps[enumType]
} }
// A registry of all linked message types.
// The string is a fully-qualified proto name ("pkg.Message").
var (
protoTypes = make(map[string]reflect.Type)
revProtoTypes = make(map[reflect.Type]string)
)
// RegisterType is called from generated code and maps from the fully qualified
// proto name to the type (pointer to struct) of the protocol buffer.
func RegisterType(x Message, name string) {
if _, ok := protoTypes[name]; ok {
// TODO: Some day, make this a panic.
log.Printf("proto: duplicate proto type registered: %s", name)
return
}
t := reflect.TypeOf(x)
protoTypes[name] = t
revProtoTypes[t] = name
}
// MessageName returns the fully-qualified proto name for the given message type.
func MessageName(x Message) string { return revProtoTypes[reflect.TypeOf(x)] }
// MessageType returns the message type (pointer to struct) for a named message.
func MessageType(name string) reflect.Type { return protoTypes[name] }

View File

@@ -0,0 +1,27 @@
Copyright (c) 2013 Google. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -22,7 +22,6 @@ package query
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"net/url" "net/url"
"reflect" "reflect"
@@ -88,7 +87,9 @@ type Encoder interface {
// Slice and Array values default to encoding as multiple URL values of the // Slice and Array values default to encoding as multiple URL values of the
// same name. Including the "comma" option signals that the field should be // same name. Including the "comma" option signals that the field should be
// encoded as a single comma-delimited value. Including the "space" option // encoded as a single comma-delimited value. Including the "space" option
// similarly encodes the value as a single space-delimited string. // similarly encodes the value as a single space-delimited string. Including
// the "brackets" option signals that the multiple URL values should have "[]"
// appended to the value name.
// //
// Anonymous struct fields are usually encoded as if their inner exported // Anonymous struct fields are usually encoded as if their inner exported
// fields were fields in the outer struct, subject to the standard Go // fields were fields in the outer struct, subject to the standard Go
@@ -97,32 +98,41 @@ type Encoder interface {
// //
// Non-nil pointer values are encoded as the value pointed to. // Non-nil pointer values are encoded as the value pointed to.
// //
// Nested structs are encoded including parent fields in value names for
// scoping. e.g:
//
// "user[name]=acme&user[addr][postcode]=1234&user[addr][city]=SFO"
//
// All other values are encoded using their default string representation. // All other values are encoded using their default string representation.
// //
// Multiple fields that encode to the same URL parameter name will be included // Multiple fields that encode to the same URL parameter name will be included
// as multiple URL values of the same name. // as multiple URL values of the same name.
func Values(v interface{}) (url.Values, error) { func Values(v interface{}) (url.Values, error) {
values := make(url.Values)
val := reflect.ValueOf(v) val := reflect.ValueOf(v)
for val.Kind() == reflect.Ptr { for val.Kind() == reflect.Ptr {
if val.IsNil() { if val.IsNil() {
return nil, errors.New("query: Values() expects non-nil value") return values, nil
} }
val = val.Elem() val = val.Elem()
} }
if v == nil {
return values, nil
}
if val.Kind() != reflect.Struct { if val.Kind() != reflect.Struct {
return nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind()) return nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind())
} }
values := make(url.Values) err := reflectValue(values, val, "")
err := reflectValue(values, val)
return values, err return values, err
} }
// reflectValue populates the values parameter from the struct fields in val. // reflectValue populates the values parameter from the struct fields in val.
// Embedded structs are followed recursively (using the rules defined in the // Embedded structs are followed recursively (using the rules defined in the
// Values function documentation) breadth-first. // Values function documentation) breadth-first.
func reflectValue(values url.Values, val reflect.Value) error { func reflectValue(values url.Values, val reflect.Value, scope string) error {
var embedded []reflect.Value var embedded []reflect.Value
typ := val.Type() typ := val.Type()
@@ -148,11 +158,19 @@ func reflectValue(values url.Values, val reflect.Value) error {
name = sf.Name name = sf.Name
} }
if scope != "" {
name = scope + "[" + name + "]"
}
if opts.Contains("omitempty") && isEmptyValue(sv) { if opts.Contains("omitempty") && isEmptyValue(sv) {
continue continue
} }
if sv.Type().Implements(encoderType) { if sv.Type().Implements(encoderType) {
if !reflect.Indirect(sv).IsValid() {
sv = reflect.New(sv.Type().Elem())
}
m := sv.Interface().(Encoder) m := sv.Interface().(Encoder)
if err := m.EncodeValues(name, &values); err != nil { if err := m.EncodeValues(name, &values); err != nil {
return err return err
@@ -166,6 +184,8 @@ func reflectValue(values url.Values, val reflect.Value) error {
del = ',' del = ','
} else if opts.Contains("space") { } else if opts.Contains("space") {
del = ' ' del = ' '
} else if opts.Contains("brackets") {
name = name + "[]"
} }
if del != 0 { if del != 0 {
@@ -188,11 +208,28 @@ func reflectValue(values url.Values, val reflect.Value) error {
continue continue
} }
if sv.Type() == timeType {
values.Add(name, valueString(sv, opts))
continue
}
for sv.Kind() == reflect.Ptr {
if sv.IsNil() {
break
}
sv = sv.Elem()
}
if sv.Kind() == reflect.Struct {
reflectValue(values, sv, name)
continue
}
values.Add(name, valueString(sv, opts)) values.Add(name, valueString(sv, opts))
} }
for _, f := range embedded { for _, f := range embedded {
if err := reflectValue(values, f); err != nil { if err := reflectValue(values, f, scope); err != nil {
return err return err
} }
} }

View File

@@ -23,7 +23,7 @@ For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/col
Usage is easy enough: Usage is easy enough:
```go ```go
fmt.Println(colorstring.Color("[blue]Hello [red]World!")) colorstring.Println("[blue]Hello [red]World!")
``` ```
Additionally, the `Colorize` struct can be used to set options such as Additionally, the `Colorize` struct can be used to set options such as

View File

@@ -5,7 +5,9 @@ package colorstring
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"regexp" "regexp"
"strings"
) )
// Color colorizes your strings using the default settings. // Color colorizes your strings using the default settings.
@@ -26,6 +28,15 @@ func Color(v string) string {
return def.Color(v) return def.Color(v)
} }
// ColorPrefix returns the color sequence that prefixes the given text.
//
// This is useful when wrapping text if you want to inherit the color
// of the wrapped text. For example, "[green]foo" will return "[green]".
// If there is no color sequence, then this will return "".
func ColorPrefix(v string) string {
return def.ColorPrefix(v)
}
// Colorize colorizes your strings, giving you the ability to customize // Colorize colorizes your strings, giving you the ability to customize
// some of the colorization process. // some of the colorization process.
// //
@@ -89,6 +100,15 @@ func (c *Colorize) Color(v string) string {
return result.String() return result.String()
} }
// ColorPrefix returns the first color sequence that exists in this string.
//
// For example: "[green]foo" would return "[green]". If no color sequence
// exists, then "" is returned. This is especially useful when wrapping
// colored texts to inherit the color of the wrapped text.
func (c *Colorize) ColorPrefix(v string) string {
return prefixRe.FindString(strings.TrimSpace(v))
}
// DefaultColors are the default colors used when colorizing. // DefaultColors are the default colors used when colorizing.
// //
// If the color is surrounded in underscores, such as "_blue_", then that // If the color is surrounded in underscores, such as "_blue_", then that
@@ -158,4 +178,67 @@ func init() {
} }
var def Colorize var def Colorize
var parseRe = regexp.MustCompile(`(?i)\[[a-z0-9_-]+\]`) var parseReRaw = `\[[a-z0-9_-]+\]`
var parseRe = regexp.MustCompile(`(?i)` + parseReRaw)
var prefixRe = regexp.MustCompile(`^(?i)(` + parseReRaw + `)+`)
// Print is a convenience wrapper for fmt.Print with support for color codes.
//
// Print formats using the default formats for its operands and writes to
// standard output with support for color codes. Spaces are added between
// operands when neither is a string. It returns the number of bytes written
// and any write error encountered.
func Print(a string) (n int, err error) {
return fmt.Print(Color(a))
}
// Println is a convenience wrapper for fmt.Println with support for color
// codes.
//
// Println formats using the default formats for its operands and writes to
// standard output with support for color codes. Spaces are always added
// between operands and a newline is appended. It returns the number of bytes
// written and any write error encountered.
func Println(a string) (n int, err error) {
return fmt.Println(Color(a))
}
// Printf is a convenience wrapper for fmt.Printf with support for color codes.
//
// Printf formats according to a format specifier and writes to standard output
// with support for color codes. It returns the number of bytes written and any
// write error encountered.
func Printf(format string, a ...interface{}) (n int, err error) {
return fmt.Printf(Color(format), a...)
}
// Fprint is a convenience wrapper for fmt.Fprint with support for color codes.
//
// Fprint formats using the default formats for its operands and writes to w
// with support for color codes. Spaces are added between operands when neither
// is a string. It returns the number of bytes written and any write error
// encountered.
func Fprint(w io.Writer, a string) (n int, err error) {
return fmt.Fprint(w, Color(a))
}
// Fprintln is a convenience wrapper for fmt.Fprintln with support for color
// codes.
//
// Fprintln formats using the default formats for its operands and writes to w
// with support for color codes. Spaces are always added between operands and a
// newline is appended. It returns the number of bytes written and any write
// error encountered.
func Fprintln(w io.Writer, a string) (n int, err error) {
return fmt.Fprintln(w, Color(a))
}
// Fprintf is a convenience wrapper for fmt.Fprintf with support for color
// codes.
//
// Fprintf formats according to a format specifier and writes to w with support
// for color codes. It returns the number of bytes written and any write error
// encountered.
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
return fmt.Fprintf(w, Color(format), a...)
}

View File

@@ -1,12 +1,15 @@
language: go language: go
go: go:
- 1.3
- 1.4 - 1.4
- 1.5
- tip
install: install:
- go get -v ./... - go get -v -t ./...
- go get golang.org/x/tools/cmd/cover - go get golang.org/x/tools/cmd/cover
- go get github.com/onsi/gomega - go get github.com/onsi/gomega
- go install github.com/onsi/ginkgo/ginkgo - go install github.com/onsi/ginkgo/ginkgo
- export PATH=$PATH:$HOME/gopath/bin - export PATH=$PATH:$HOME/gopath/bin
script: $HOME/gopath/bin/ginkgo -r --randomizeAllSpecs --failOnPending --randomizeSuites --race script: $HOME/gopath/bin/ginkgo -r --randomizeAllSpecs --randomizeSuites --race --trace

View File

@@ -2,6 +2,27 @@
Improvements: Improvements:
- `Skip(message)` can be used to skip the current test.
- Added `extensions/table` - a Ginkgo DSL for [Table Driven Tests](http://onsi.github.io/ginkgo/#table-driven-tests)
Bug Fixes:
- Ginkgo tests now fail when you `panic(nil)` (#167)
## 1.2.0 5/31/2015
Improvements
- `ginkgo -coverpkg` calls down to `go test -coverpkg` (#160)
- `ginkgo -afterSuiteHook COMMAND` invokes the passed-in `COMMAND` after a test suite completes (#152)
- Relaxed requirement for Go 1.4+. `ginkgo` now works with Go v1.3+ (#166)
## 1.2.0-beta
Ginkgo now requires Go 1.4+
Improvements:
- Call reporters in reverse order when announcing spec completion -- allows custom reporters to emit output before the default reporter does. - Call reporters in reverse order when announcing spec completion -- allows custom reporters to emit output before the default reporter does.
- Improved focus behavior. Now, this: - Improved focus behavior. Now, this:
@@ -26,13 +47,18 @@ Improvements:
- Support `bootstrap`ping and `generate`ing [Agouti](http://agouti.org) specs. - Support `bootstrap`ping and `generate`ing [Agouti](http://agouti.org) specs.
- `ginkgo generate` and `ginkgo bootstrap` now honor the package name already defined in a given directory - `ginkgo generate` and `ginkgo bootstrap` now honor the package name already defined in a given directory
- The `ginkgo` CLI ignores `SIGQUIT`. Prevents its stack dump from interlacing with the underlying test suite's stack dump. - The `ginkgo` CLI ignores `SIGQUIT`. Prevents its stack dump from interlacing with the underlying test suite's stack dump.
- The `ginkgo` CLI now compiles tests into a temporary directory instead of the package directory. This necessitates upgrading to Go v1.4+.
- `ginkgo -notify` now works on Linux
Bug Fixes: Bug Fixes:
- If --skipPackages is used and all packages are skipped, Ginkgo should exit 0. - If --skipPackages is used and all packages are skipped, Ginkgo should exit 0.
- Fix tempfile leak when running in parallel - Fix tempfile leak when running in parallel
- Fix incorrect failure message when a panic occurs during a parallel test run - Fix incorrect failure message when a panic occurs during a parallel test run
- Fixed an issue where a pending test within a focused context (or a focused test within a pending context) would skip all other tests.
- Be more consistent about handling SIGTERM as well as SIGINT
- When interupted while concurrently compiling test suites in the background, Ginkgo now cleans up the compiled artifacts.
- Fixed a long standing bug where `ginkgo -p` would hang if a process spawned by one of the Ginkgo parallel nodes does not exit. (Hooray!)
## 1.1.0 (8/2/2014) ## 1.1.0 (8/2/2014)

View File

@@ -59,7 +59,7 @@ Agouti allows you run WebDriver integration tests. Learn more about Agouti [her
## Set Me Up! ## Set Me Up!
You'll need Golang v1.2+ (Ubuntu users: you probably have Golang v1.0 -- you'll need to upgrade!) You'll need Golang v1.3+ (Ubuntu users: you probably have Golang v1.0 -- you'll need to upgrade!)
```bash ```bash

View File

@@ -20,7 +20,7 @@ import (
"fmt" "fmt"
) )
const VERSION = "1.1.0" const VERSION = "1.2.0"
type GinkgoConfigType struct { type GinkgoConfigType struct {
RandomSeed int64 RandomSeed int64

View File

@@ -0,0 +1,98 @@
/*
Table provides a simple DSL for Ginkgo-native Table-Driven Tests
The godoc documentation describes Table's API. More comprehensive documentation (with examples!) is available at http://onsi.github.io/ginkgo#table-driven-tests
*/
package table
import (
"fmt"
"reflect"
"github.com/onsi/ginkgo"
)
/*
DescribeTable describes a table-driven test.
For example:
DescribeTable("a simple table",
func(x int, y int, expected bool) {
Ω(x > y).Should(Equal(expected))
},
Entry("x > y", 1, 0, true),
Entry("x == y", 0, 0, false),
Entry("x < y", 0, 1, false),
)
The first argument to `DescribeTable` is a string description.
The second argument is a function that will be run for each table entry. Your assertions go here - the function is equivalent to a Ginkgo It.
The subsequent arguments must be of type `TableEntry`. We recommend using the `Entry` convenience constructors.
The `Entry` constructor takes a string description followed by an arbitrary set of parameters. These parameters are passed into your function.
Under the hood, `DescribeTable` simply generates a new Ginkgo `Describe`. Each `Entry` is turned into an `It` within the `Describe`.
It's important to understand that the `Describe`s and `It`s are generated at evaluation time (i.e. when Ginkgo constructs the tree of tests and before the tests run).
Individual Entries can be focused (with FEntry) or marked pending (with PEntry or XEntry). In addition, the entire table can be focused or marked pending with FDescribeTable and PDescribeTable/XDescribeTable.
*/
func DescribeTable(description string, itBody interface{}, entries ...TableEntry) bool {
describeTable(description, itBody, entries, false, false)
return true
}
/*
You can focus a table with `FDescribeTable`. This is equivalent to `FDescribe`.
*/
func FDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool {
describeTable(description, itBody, entries, false, true)
return true
}
/*
You can mark a table as pending with `PDescribeTable`. This is equivalent to `PDescribe`.
*/
func PDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool {
describeTable(description, itBody, entries, true, false)
return true
}
/*
You can mark a table as pending with `XDescribeTable`. This is equivalent to `XDescribe`.
*/
func XDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool {
describeTable(description, itBody, entries, true, false)
return true
}
func describeTable(description string, itBody interface{}, entries []TableEntry, pending bool, focused bool) {
itBodyValue := reflect.ValueOf(itBody)
if itBodyValue.Kind() != reflect.Func {
panic(fmt.Sprintf("DescribeTable expects a function, got %#v", itBody))
}
if pending {
ginkgo.PDescribe(description, func() {
for _, entry := range entries {
entry.generateIt(itBodyValue)
}
})
} else if focused {
ginkgo.FDescribe(description, func() {
for _, entry := range entries {
entry.generateIt(itBodyValue)
}
})
} else {
ginkgo.Describe(description, func() {
for _, entry := range entries {
entry.generateIt(itBodyValue)
}
})
}
}

View File

@@ -0,0 +1,72 @@
package table
import (
"reflect"
"github.com/onsi/ginkgo"
)
/*
TableEntry represents an entry in a table test. You generally use the `Entry` constructor.
*/
type TableEntry struct {
Description string
Parameters []interface{}
Pending bool
Focused bool
}
func (t TableEntry) generateIt(itBody reflect.Value) {
if t.Pending {
ginkgo.PIt(t.Description)
return
}
values := []reflect.Value{}
for _, param := range t.Parameters {
values = append(values, reflect.ValueOf(param))
}
body := func() {
itBody.Call(values)
}
if t.Focused {
ginkgo.FIt(t.Description, body)
} else {
ginkgo.It(t.Description, body)
}
}
/*
Entry constructs a TableEntry.
The first argument is a required description (this becomes the content of the generated Ginkgo `It`).
Subsequent parameters are saved off and sent to the callback passed in to `DescribeTable`.
Each Entry ends up generating an individual Ginkgo It.
*/
func Entry(description string, parameters ...interface{}) TableEntry {
return TableEntry{description, parameters, false, false}
}
/*
You can focus a particular entry with FEntry. This is equivalent to FIt.
*/
func FEntry(description string, parameters ...interface{}) TableEntry {
return TableEntry{description, parameters, false, true}
}
/*
You can mark a particular entry as pending with PEntry. This is equivalent to PIt.
*/
func PEntry(description string, parameters ...interface{}) TableEntry {
return TableEntry{description, parameters, true, false}
}
/*
You can mark a particular entry as pending with XEntry. This is equivalent to XIt.
*/
func XEntry(description string, parameters ...interface{}) TableEntry {
return TableEntry{description, parameters, true, false}
}

View File

@@ -54,7 +54,7 @@ var agoutiBootstrapText = `package {{.Package}}_test
import ( import (
{{.GinkgoImport}} {{.GinkgoImport}}
{{.GomegaImport}} {{.GomegaImport}}
. "github.com/sclevine/agouti/core" "github.com/sclevine/agouti"
"testing" "testing"
) )
@@ -64,23 +64,20 @@ func Test{{.FormattedName}}(t *testing.T) {
RunSpecs(t, "{{.FormattedName}} Suite") RunSpecs(t, "{{.FormattedName}} Suite")
} }
var agoutiDriver WebDriver var agoutiDriver *agouti.WebDriver
var _ = BeforeSuite(func() { var _ = BeforeSuite(func() {
var err error
// Choose a WebDriver: // Choose a WebDriver:
agoutiDriver, err = PhantomJS() agoutiDriver = agouti.PhantomJS()
// agoutiDriver, err = Selenium() // agoutiDriver = agouti.Selenium()
// agoutiDriver, err = Chrome() // agoutiDriver = agouti.ChromeDriver()
Expect(err).NotTo(HaveOccurred())
Expect(agoutiDriver.Start()).To(Succeed()) Expect(agoutiDriver.Start()).To(Succeed())
}) })
var _ = AfterSuite(func() { var _ = AfterSuite(func() {
agoutiDriver.Stop() Expect(agoutiDriver.Stop()).To(Succeed())
}) })
` `

View File

@@ -46,15 +46,19 @@ func (r *SpecBuilder) BuildSpecs(args []string, additionalArgs []string) {
passed := true passed := true
for _, suite := range suites { for _, suite := range suites {
runner := testrunner.New(suite, 1, false, r.commandFlags.Race, r.commandFlags.Cover, r.commandFlags.Tags, nil) runner := testrunner.New(suite, 1, false, r.commandFlags.Race, r.commandFlags.Cover, r.commandFlags.CoverPkg, r.commandFlags.Tags, nil)
fmt.Printf("Compiling %s...\n", suite.PackageName) fmt.Printf("Compiling %s...\n", suite.PackageName)
err := runner.Compile()
path, _ := filepath.Abs(filepath.Join(suite.Path, fmt.Sprintf("%s.test", suite.PackageName)))
err := runner.CompileTo(path)
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Println(err.Error())
passed = false passed = false
} else { } else {
fmt.Printf(" compiled %s.test\n", filepath.Join(suite.Path, suite.PackageName)) fmt.Printf(" compiled %s.test\n", suite.PackageName)
} }
runner.CleanUp()
} }
if passed { if passed {

View File

@@ -51,21 +51,21 @@ import (
{{if .IncludeImports}}. "github.com/onsi/ginkgo"{{end}} {{if .IncludeImports}}. "github.com/onsi/ginkgo"{{end}}
{{if .IncludeImports}}. "github.com/onsi/gomega"{{end}} {{if .IncludeImports}}. "github.com/onsi/gomega"{{end}}
. "github.com/sclevine/agouti/core"
. "github.com/sclevine/agouti/matchers" . "github.com/sclevine/agouti/matchers"
"github.com/sclevine/agouti"
) )
var _ = Describe("{{.Subject}}", func() { var _ = Describe("{{.Subject}}", func() {
var page Page var page *agouti.Page
BeforeEach(func() { BeforeEach(func() {
var err error var err error
page, err = agoutiDriver.Page() page, err = agoutiDriver.NewPage()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
}) })
AfterEach(func() { AfterEach(func() {
page.Destroy() Expect(page.Destroy()).To(Succeed())
}) })
}) })
` `

View File

@@ -4,6 +4,7 @@ import (
"os" "os"
"os/signal" "os/signal"
"sync" "sync"
"syscall"
) )
type InterruptHandler struct { type InterruptHandler struct {
@@ -33,7 +34,7 @@ func (h *InterruptHandler) WasInterrupted() bool {
func (h *InterruptHandler) handleInterrupt() { func (h *InterruptHandler) handleInterrupt() {
c := make(chan os.Signal, 1) c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt) signal.Notify(c, os.Interrupt, syscall.SIGTERM)
<-c <-c
signal.Stop(c) signal.Stop(c)

View File

@@ -58,7 +58,7 @@ passing `ginkgo watch` the `-r` flag will recursively detect all test suites und
`watch` does not detect *new* packages. Moreover, changes in package X only rerun the tests for package X, tests for packages `watch` does not detect *new* packages. Moreover, changes in package X only rerun the tests for package X, tests for packages
that depend on X are not rerun. that depend on X are not rerun.
[OSX only] To receive (desktop) notifications when a test run completes: [OSX & Linux only] To receive (desktop) notifications when a test run completes:
ginkgo -notify ginkgo -notify

View File

@@ -4,7 +4,11 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"regexp"
"runtime"
"strings"
"github.com/onsi/ginkgo/config"
"github.com/onsi/ginkgo/ginkgo/testsuite" "github.com/onsi/ginkgo/ginkgo/testsuite"
) )
@@ -20,10 +24,16 @@ func NewNotifier(commandFlags *RunWatchAndBuildCommandFlags) *Notifier {
func (n *Notifier) VerifyNotificationsAreAvailable() { func (n *Notifier) VerifyNotificationsAreAvailable() {
if n.commandFlags.Notify { if n.commandFlags.Notify {
onLinux := (runtime.GOOS == "linux")
onOSX := (runtime.GOOS == "darwin")
if onOSX {
_, err := exec.LookPath("terminal-notifier") _, err := exec.LookPath("terminal-notifier")
if err != nil { if err != nil {
fmt.Printf(`--notify requires terminal-notifier, which you don't seem to have installed. fmt.Printf(`--notify requires terminal-notifier, which you don't seem to have installed.
OSX:
To remedy this: To remedy this:
brew install terminal-notifier brew install terminal-notifier
@@ -34,6 +44,21 @@ To learn more about terminal-notifier:
`) `)
os.Exit(1) os.Exit(1)
} }
} else if onLinux {
_, err := exec.LookPath("notify-send")
if err != nil {
fmt.Printf(`--notify requires terminal-notifier or notify-send, which you don't seem to have installed.
Linux:
Download and install notify-send for your distribution
`)
os.Exit(1)
}
}
} }
} }
@@ -46,8 +71,16 @@ func (n *Notifier) SendSuiteCompletionNotification(suite testsuite.TestSuite, su
} }
func (n *Notifier) SendNotification(title string, subtitle string) { func (n *Notifier) SendNotification(title string, subtitle string) {
args := []string{"-title", title, "-subtitle", subtitle, "-group", "com.onsi.ginkgo"}
if n.commandFlags.Notify {
onLinux := (runtime.GOOS == "linux")
onOSX := (runtime.GOOS == "darwin")
if onOSX {
_, err := exec.LookPath("terminal-notifier")
if err == nil {
args := []string{"-title", title, "-subtitle", subtitle, "-group", "com.onsi.ginkgo"}
terminal := os.Getenv("TERM_PROGRAM") terminal := os.Getenv("TERM_PROGRAM")
if terminal == "iTerm.app" { if terminal == "iTerm.app" {
args = append(args, "-activate", "com.googlecode.iterm2") args = append(args, "-activate", "com.googlecode.iterm2")
@@ -55,7 +88,54 @@ func (n *Notifier) SendNotification(title string, subtitle string) {
args = append(args, "-activate", "com.apple.Terminal") args = append(args, "-activate", "com.apple.Terminal")
} }
if n.commandFlags.Notify {
exec.Command("terminal-notifier", args...).Run() exec.Command("terminal-notifier", args...).Run()
} }
} else if onLinux {
_, err := exec.LookPath("notify-send")
if err == nil {
args := []string{"-a", "ginkgo", title, subtitle}
exec.Command("notify-send", args...).Run()
}
}
}
}
func (n *Notifier) RunCommand(suite testsuite.TestSuite, suitePassed bool) {
command := n.commandFlags.AfterSuiteHook
if command != "" {
// Allow for string replacement to pass input to the command
passed := "[FAIL]"
if suitePassed {
passed = "[PASS]"
}
command = strings.Replace(command, "(ginkgo-suite-passed)", passed, -1)
command = strings.Replace(command, "(ginkgo-suite-name)", suite.PackageName, -1)
// Must break command into parts
splitArgs := regexp.MustCompile(`'.+'|".+"|\S+`)
parts := splitArgs.FindAllString(command, -1)
output, err := exec.Command(parts[0], parts[1:]...).CombinedOutput()
if err != nil {
fmt.Println("Post-suite command failed:")
if config.DefaultReporterConfig.NoColor {
fmt.Printf("\t%s\n", output)
} else {
fmt.Printf("\t%s%s%s\n", redColor, string(output), defaultStyle)
}
n.SendNotification("Ginkgo [ERROR]", fmt.Sprintf(`After suite command "%s" failed`, n.commandFlags.AfterSuiteHook))
} else {
fmt.Println("Post-suite command succeeded:")
if config.DefaultReporterConfig.NoColor {
fmt.Printf("\t%s\n", output)
} else {
fmt.Printf("\t%s%s%s\n", greenColor, string(output), defaultStyle)
}
}
}
} }

View File

@@ -71,7 +71,7 @@ func (r *SpecRunner) RunSpecs(args []string, additionalArgs []string) {
runners := []*testrunner.TestRunner{} runners := []*testrunner.TestRunner{}
for _, suite := range suites { for _, suite := range suites {
runners = append(runners, testrunner.New(suite, r.commandFlags.NumCPU, r.commandFlags.ParallelStream, r.commandFlags.Race, r.commandFlags.Cover, r.commandFlags.Tags, additionalArgs)) runners = append(runners, testrunner.New(suite, r.commandFlags.NumCPU, r.commandFlags.ParallelStream, r.commandFlags.Race, r.commandFlags.Cover, r.commandFlags.CoverPkg, r.commandFlags.Tags, additionalArgs))
} }
numSuites := 0 numSuites := 0

View File

@@ -11,6 +11,7 @@ type RunWatchAndBuildCommandFlags struct {
Recurse bool Recurse bool
Race bool Race bool
Cover bool Cover bool
CoverPkg string
SkipPackage string SkipPackage string
Tags string Tags string
@@ -19,6 +20,7 @@ type RunWatchAndBuildCommandFlags struct {
NumCompilers int NumCompilers int
ParallelStream bool ParallelStream bool
Notify bool Notify bool
AfterSuiteHook string
AutoNodes bool AutoNodes bool
//only for run command //only for run command
@@ -87,11 +89,11 @@ func (c *RunWatchAndBuildCommandFlags) computeNodes() {
func (c *RunWatchAndBuildCommandFlags) flags(mode int) { func (c *RunWatchAndBuildCommandFlags) flags(mode int) {
onWindows := (runtime.GOOS == "windows") onWindows := (runtime.GOOS == "windows")
onOSX := (runtime.GOOS == "darwin")
c.FlagSet.BoolVar(&(c.Recurse), "r", false, "Find and run test suites under the current directory recursively") c.FlagSet.BoolVar(&(c.Recurse), "r", false, "Find and run test suites under the current directory recursively")
c.FlagSet.BoolVar(&(c.Race), "race", false, "Run tests with race detection enabled") c.FlagSet.BoolVar(&(c.Race), "race", false, "Run tests with race detection enabled")
c.FlagSet.BoolVar(&(c.Cover), "cover", false, "Run tests with coverage analysis, will generate coverage profiles with the package name in the current directory") c.FlagSet.BoolVar(&(c.Cover), "cover", false, "Run tests with coverage analysis, will generate coverage profiles with the package name in the current directory")
c.FlagSet.StringVar(&(c.CoverPkg), "coverpkg", "", "Run tests with coverage on the given external modules")
c.FlagSet.StringVar(&(c.SkipPackage), "skipPackage", "", "A comma-separated list of package names to be skipped. If any part of the package's path matches, that package is ignored.") c.FlagSet.StringVar(&(c.SkipPackage), "skipPackage", "", "A comma-separated list of package names to be skipped. If any part of the package's path matches, that package is ignored.")
c.FlagSet.StringVar(&(c.Tags), "tags", "", "A list of build tags to consider satisfied during the build") c.FlagSet.StringVar(&(c.Tags), "tags", "", "A list of build tags to consider satisfied during the build")
@@ -101,9 +103,10 @@ func (c *RunWatchAndBuildCommandFlags) flags(mode int) {
c.FlagSet.IntVar(&(c.NumCompilers), "compilers", 0, "The number of concurrent compilations to run (0 will autodetect)") c.FlagSet.IntVar(&(c.NumCompilers), "compilers", 0, "The number of concurrent compilations to run (0 will autodetect)")
c.FlagSet.BoolVar(&(c.AutoNodes), "p", false, "Run in parallel with auto-detected number of nodes") c.FlagSet.BoolVar(&(c.AutoNodes), "p", false, "Run in parallel with auto-detected number of nodes")
c.FlagSet.BoolVar(&(c.ParallelStream), "stream", onWindows, "stream parallel test output in real time: less coherent, but useful for debugging") c.FlagSet.BoolVar(&(c.ParallelStream), "stream", onWindows, "stream parallel test output in real time: less coherent, but useful for debugging")
if onOSX { if !onWindows {
c.FlagSet.BoolVar(&(c.Notify), "notify", false, "Send desktop notifications when a test run completes") c.FlagSet.BoolVar(&(c.Notify), "notify", false, "Send desktop notifications when a test run completes")
} }
c.FlagSet.StringVar(&(c.AfterSuiteHook), "afterSuiteHook", "", "Run a command when a suite test run completes")
} }
if mode == runMode { if mode == runMode {

View File

@@ -3,6 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"runtime" "runtime"
"sync"
"github.com/onsi/ginkgo/config" "github.com/onsi/ginkgo/config"
"github.com/onsi/ginkgo/ginkgo/interrupthandler" "github.com/onsi/ginkgo/ginkgo/interrupthandler"
@@ -10,28 +11,21 @@ import (
"github.com/onsi/ginkgo/ginkgo/testsuite" "github.com/onsi/ginkgo/ginkgo/testsuite"
) )
type compilationInput struct {
runner *testrunner.TestRunner
result chan compilationOutput
}
type compilationOutput struct {
runner *testrunner.TestRunner
err error
}
type SuiteRunner struct { type SuiteRunner struct {
notifier *Notifier notifier *Notifier
interruptHandler *interrupthandler.InterruptHandler interruptHandler *interrupthandler.InterruptHandler
} }
type compiler struct {
runner *testrunner.TestRunner
compilationError chan error
}
func (c *compiler) compile() {
retries := 0
err := c.runner.Compile()
for err != nil && retries < 5 { //We retry because Go sometimes steps on itself when multiple compiles happen in parallel. This is ugly, but should help resolve flakiness...
err = c.runner.Compile()
retries++
}
c.compilationError <- err
}
func NewSuiteRunner(notifier *Notifier, interruptHandler *interrupthandler.InterruptHandler) *SuiteRunner { func NewSuiteRunner(notifier *Notifier, interruptHandler *interrupthandler.InterruptHandler) *SuiteRunner {
return &SuiteRunner{ return &SuiteRunner{
notifier: notifier, notifier: notifier,
@@ -39,63 +33,111 @@ func NewSuiteRunner(notifier *Notifier, interruptHandler *interrupthandler.Inter
} }
} }
func (r *SuiteRunner) RunSuites(runners []*testrunner.TestRunner, numCompilers int, keepGoing bool, willCompile func(suite testsuite.TestSuite)) (testrunner.RunResult, int) { func (r *SuiteRunner) compileInParallel(runners []*testrunner.TestRunner, numCompilers int, willCompile func(suite testsuite.TestSuite)) chan compilationOutput {
runResult := testrunner.PassingRunResult() //we return this to the consumer, it will return each runner in order as it compiles
compilationOutputs := make(chan compilationOutput, len(runners))
compilers := make([]*compiler, len(runners)) //an array of channels - the nth runner's compilation output is sent to the nth channel in this array
//we read from these channels in order to ensure we run the suites in order
orderedCompilationOutputs := []chan compilationOutput{}
for _ = range runners {
orderedCompilationOutputs = append(orderedCompilationOutputs, make(chan compilationOutput, 1))
}
//we're going to spin up numCompilers compilers - they're going to run concurrently and will consume this channel
//we prefill the channel then close it, this ensures we compile things in the correct order
workPool := make(chan compilationInput, len(runners))
for i, runner := range runners { for i, runner := range runners {
compilers[i] = &compiler{ workPool <- compilationInput{runner, orderedCompilationOutputs[i]}
runner: runner,
compilationError: make(chan error, 1),
}
} }
close(workPool)
compilerChannel := make(chan *compiler) //pick a reasonable numCompilers
if numCompilers == 0 { if numCompilers == 0 {
numCompilers = runtime.NumCPU() numCompilers = runtime.NumCPU()
} }
//a WaitGroup to help us wait for all compilers to shut down
wg := &sync.WaitGroup{}
wg.Add(numCompilers)
//spin up the concurrent compilers
for i := 0; i < numCompilers; i++ { for i := 0; i < numCompilers; i++ {
go func() { go func() {
for compiler := range compilerChannel { defer wg.Done()
for input := range workPool {
if r.interruptHandler.WasInterrupted() {
return
}
if willCompile != nil { if willCompile != nil {
willCompile(compiler.runner.Suite) willCompile(input.runner.Suite)
} }
compiler.compile()
//We retry because Go sometimes steps on itself when multiple compiles happen in parallel. This is ugly, but should help resolve flakiness...
var err error
retries := 0
for retries <= 5 {
if r.interruptHandler.WasInterrupted() {
return
}
if err = input.runner.Compile(); err == nil {
break
}
retries++
}
input.result <- compilationOutput{input.runner, err}
} }
}() }()
} }
//read from the compilation output channels *in order* and send them to the caller
//close the compilationOutputs channel to tell the caller we're done
go func() { go func() {
for _, compiler := range compilers { defer close(compilationOutputs)
compilerChannel <- compiler for _, orderedCompilationOutput := range orderedCompilationOutputs {
select {
case compilationOutput := <-orderedCompilationOutput:
compilationOutputs <- compilationOutput
case <-r.interruptHandler.C:
//interrupt detected, wait for the compilers to shut down then bail
//this ensure we clean up after ourselves as we don't leave any compilation processes running
wg.Wait()
return
}
} }
close(compilerChannel)
}() }()
return compilationOutputs
}
func (r *SuiteRunner) RunSuites(runners []*testrunner.TestRunner, numCompilers int, keepGoing bool, willCompile func(suite testsuite.TestSuite)) (testrunner.RunResult, int) {
runResult := testrunner.PassingRunResult()
compilationOutputs := r.compileInParallel(runners, numCompilers, willCompile)
numSuitesThatRan := 0 numSuitesThatRan := 0
suitesThatFailed := []testsuite.TestSuite{} suitesThatFailed := []testsuite.TestSuite{}
for i, runner := range runners { for compilationOutput := range compilationOutputs {
if r.interruptHandler.WasInterrupted() { if compilationOutput.err != nil {
break fmt.Print(compilationOutput.err.Error())
}
compilationError := <-compilers[i].compilationError
if compilationError != nil {
fmt.Print(compilationError.Error())
} }
numSuitesThatRan++ numSuitesThatRan++
suiteRunResult := testrunner.FailingRunResult() suiteRunResult := testrunner.FailingRunResult()
if compilationError == nil { if compilationOutput.err == nil {
suiteRunResult = compilers[i].runner.Run() suiteRunResult = compilationOutput.runner.Run()
} }
r.notifier.SendSuiteCompletionNotification(runner.Suite, suiteRunResult.Passed) r.notifier.SendSuiteCompletionNotification(compilationOutput.runner.Suite, suiteRunResult.Passed)
r.notifier.RunCommand(compilationOutput.runner.Suite, suiteRunResult.Passed)
runResult = runResult.Merge(suiteRunResult) runResult = runResult.Merge(suiteRunResult)
if !suiteRunResult.Passed { if !suiteRunResult.Passed {
suitesThatFailed = append(suitesThatFailed, runner.Suite) suitesThatFailed = append(suitesThatFailed, compilationOutput.runner.Suite)
if !keepGoing { if !keepGoing {
break break
} }
} }
if i < len(runners)-1 && !config.DefaultReporterConfig.Succinct { if numSuitesThatRan < len(runners) && !config.DefaultReporterConfig.Succinct {
fmt.Println("") fmt.Println("")
} }
} }

View File

@@ -23,29 +23,47 @@ import (
type TestRunner struct { type TestRunner struct {
Suite testsuite.TestSuite Suite testsuite.TestSuite
compiled bool compiled bool
compilationTargetPath string
numCPU int numCPU int
parallelStream bool parallelStream bool
race bool race bool
cover bool cover bool
coverPkg string
tags string tags string
additionalArgs []string additionalArgs []string
} }
func New(suite testsuite.TestSuite, numCPU int, parallelStream bool, race bool, cover bool, tags string, additionalArgs []string) *TestRunner { func New(suite testsuite.TestSuite, numCPU int, parallelStream bool, race bool, cover bool, coverPkg string, tags string, additionalArgs []string) *TestRunner {
return &TestRunner{ runner := &TestRunner{
Suite: suite, Suite: suite,
numCPU: numCPU, numCPU: numCPU,
parallelStream: parallelStream, parallelStream: parallelStream,
race: race, race: race,
cover: cover, cover: cover,
coverPkg: coverPkg,
tags: tags, tags: tags,
additionalArgs: additionalArgs, additionalArgs: additionalArgs,
} }
if !suite.Precompiled {
dir, err := ioutil.TempDir("", "ginkgo")
if err != nil {
panic(fmt.Sprintf("coulnd't create temporary directory... might be time to rm -rf:\n%s", err.Error()))
}
runner.compilationTargetPath = filepath.Join(dir, suite.PackageName+".test")
}
return runner
} }
func (t *TestRunner) Compile() error { func (t *TestRunner) Compile() error {
return t.CompileTo(t.compilationTargetPath)
}
func (t *TestRunner) CompileTo(path string) error {
if t.compiled { if t.compiled {
return nil return nil
} }
@@ -54,15 +72,16 @@ func (t *TestRunner) Compile() error {
return nil return nil
} }
os.Remove(t.compiledArtifact()) args := []string{"test", "-c", "-i", "-o", path}
args := []string{"test", "-c", "-i"}
if t.race { if t.race {
args = append(args, "-race") args = append(args, "-race")
} }
if t.cover { if t.cover || t.coverPkg != "" {
args = append(args, "-cover", "-covermode=atomic") args = append(args, "-cover", "-covermode=atomic")
} }
if t.coverPkg != "" {
args = append(args, fmt.Sprintf("-coverpkg=%s", t.coverPkg))
}
if t.tags != "" { if t.tags != "" {
args = append(args, fmt.Sprintf("-tags=%s", t.tags)) args = append(args, fmt.Sprintf("-tags=%s", t.tags))
} }
@@ -78,13 +97,81 @@ func (t *TestRunner) Compile() error {
if len(output) > 0 { if len(output) > 0 {
return fmt.Errorf("Failed to compile %s:\n\n%s", t.Suite.PackageName, fixedOutput) return fmt.Errorf("Failed to compile %s:\n\n%s", t.Suite.PackageName, fixedOutput)
} }
return fmt.Errorf("") return fmt.Errorf("Failed to compile %s", t.Suite.PackageName)
}
if fileExists(path) == false {
compiledFile := filepath.Join(t.Suite.Path, t.Suite.PackageName+".test")
if fileExists(compiledFile) {
// seems like we are on an old go version that does not support the -o flag on go test
// move the compiled test file to the desired location by hand
err = os.Rename(compiledFile, path)
if err != nil {
// We cannot move the file, perhaps because the source and destination
// are on different partitions. We can copy the file, however.
err = copyFile(compiledFile, path)
if err != nil {
return fmt.Errorf("Failed to copy compiled file: %s", err)
}
}
} else {
return fmt.Errorf("Failed to compile %s: output file %q could not be found", t.Suite.PackageName, path)
}
} }
t.compiled = true t.compiled = true
return nil return nil
} }
func fileExists(path string) bool {
_, err := os.Stat(path)
return err == nil || os.IsNotExist(err) == false
}
// copyFile copies the contents of the file named src to the file named
// by dst. The file will be created if it does not already exist. If the
// destination file exists, all it's contents will be replaced by the contents
// of the source file.
func copyFile(src, dst string) error {
srcInfo, err := os.Stat(src)
if err != nil {
return err
}
mode := srcInfo.Mode()
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer func() {
closeErr := out.Close()
if err == nil {
err = closeErr
}
}()
_, err = io.Copy(out, in)
if err != nil {
return err
}
err = out.Sync()
if err != nil {
return err
}
return out.Chmod(mode)
}
/* /*
go test -c -i spits package.test out into the cwd. there's no way to change this. go test -c -i spits package.test out into the cwd. there's no way to change this.
@@ -134,12 +221,7 @@ func (t *TestRunner) CleanUp() {
if t.Suite.Precompiled { if t.Suite.Precompiled {
return return
} }
os.Remove(t.compiledArtifact()) os.RemoveAll(filepath.Dir(t.compilationTargetPath))
}
func (t *TestRunner) compiledArtifact() string {
compiledArtifact, _ := filepath.Abs(filepath.Join(t.Suite.Path, fmt.Sprintf("%s.test", t.Suite.PackageName)))
return compiledArtifact
} }
func (t *TestRunner) runSerialGinkgoSuite() RunResult { func (t *TestRunner) runSerialGinkgoSuite() RunResult {
@@ -196,7 +278,7 @@ func (t *TestRunner) runAndStreamParallelGinkgoSuite() RunResult {
os.Stdout.Sync() os.Stdout.Sync()
if t.cover { if t.cover || t.coverPkg != "" {
t.combineCoverprofiles() t.combineCoverprofiles()
} }
@@ -257,21 +339,16 @@ func (t *TestRunner) runParallelGinkgoSuite() RunResult {
fmt.Println("") fmt.Println("")
case <-time.After(time.Second): case <-time.After(time.Second):
//the aggregator never got back to us! something must have gone wrong //the aggregator never got back to us! something must have gone wrong
fmt.Println("") fmt.Println(`
fmt.Println("") -------------------------------------------------------------------
fmt.Println(" ----------------------------------------------------------- ") | |
fmt.Println(" | |") | Ginkgo timed out waiting for all parallel nodes to report back! |
fmt.Println(" | Ginkgo timed out waiting for all parallel nodes to end! |") | |
fmt.Println(" | Here is some salvaged output: |") -------------------------------------------------------------------
fmt.Println(" | |") `)
fmt.Println(" ----------------------------------------------------------- ")
fmt.Println("")
fmt.Println("")
os.Stdout.Sync() os.Stdout.Sync()
time.Sleep(time.Second)
for _, writer := range writers { for _, writer := range writers {
writer.Close() writer.Close()
} }
@@ -283,7 +360,7 @@ func (t *TestRunner) runParallelGinkgoSuite() RunResult {
os.Stdout.Sync() os.Stdout.Sync()
} }
if t.cover { if t.cover || t.coverPkg != "" {
t.combineCoverprofiles() t.combineCoverprofiles()
} }
@@ -291,8 +368,8 @@ func (t *TestRunner) runParallelGinkgoSuite() RunResult {
} }
func (t *TestRunner) cmd(ginkgoArgs []string, stream io.Writer, node int) *exec.Cmd { func (t *TestRunner) cmd(ginkgoArgs []string, stream io.Writer, node int) *exec.Cmd {
args := []string{"-test.timeout=24h"} args := []string{"--test.timeout=24h"}
if t.cover { if t.cover || t.coverPkg != "" {
coverprofile := "--test.coverprofile=" + t.Suite.PackageName + ".coverprofile" coverprofile := "--test.coverprofile=" + t.Suite.PackageName + ".coverprofile"
if t.numCPU > 1 { if t.numCPU > 1 {
coverprofile = fmt.Sprintf("%s.%d", coverprofile, node) coverprofile = fmt.Sprintf("%s.%d", coverprofile, node)
@@ -303,7 +380,12 @@ func (t *TestRunner) cmd(ginkgoArgs []string, stream io.Writer, node int) *exec.
args = append(args, ginkgoArgs...) args = append(args, ginkgoArgs...)
args = append(args, t.additionalArgs...) args = append(args, t.additionalArgs...)
cmd := exec.Command(t.compiledArtifact(), args...) path := t.compilationTargetPath
if t.Suite.Precompiled {
path, _ = filepath.Abs(filepath.Join(t.Suite.Path, fmt.Sprintf("%s.test", t.Suite.PackageName)))
}
cmd := exec.Command(path, args...)
cmd.Dir = t.Suite.Path cmd.Dir = t.Suite.Path
cmd.Stderr = stream cmd.Stderr = stream

View File

@@ -57,7 +57,7 @@ func (w *SpecWatcher) runnersForSuites(suites []testsuite.TestSuite, additionalA
runners := []*testrunner.TestRunner{} runners := []*testrunner.TestRunner{}
for _, suite := range suites { for _, suite := range suites {
runners = append(runners, testrunner.New(suite, w.commandFlags.NumCPU, w.commandFlags.ParallelStream, w.commandFlags.Race, w.commandFlags.Cover, w.commandFlags.Tags, additionalArgs)) runners = append(runners, testrunner.New(suite, w.commandFlags.NumCPU, w.commandFlags.ParallelStream, w.commandFlags.Race, w.commandFlags.Cover, w.commandFlags.CoverPkg, w.commandFlags.Tags, additionalArgs))
} }
return runners return runners
@@ -80,7 +80,7 @@ func (w *SpecWatcher) WatchSuites(args []string, additionalArgs []string) {
} }
for suite, err := range errors { for suite, err := range errors {
fmt.Printf("Failed to watch %s: %s\n"+suite.PackageName, err) fmt.Printf("Failed to watch %s: %s\n", suite.PackageName, err)
} }
if len(suites) == 1 { if len(suites) == 1 {

View File

@@ -130,6 +130,7 @@ type Done chan<- interface{}
// IsMeasurement: true if the current test is a measurement // IsMeasurement: true if the current test is a measurement
// FileName: the name of the file containing the current test // FileName: the name of the file containing the current test
// LineNumber: the line number for the current test // LineNumber: the line number for the current test
// Failed: if the current test has failed, this will be true (useful in an AfterEach)
type GinkgoTestDescription struct { type GinkgoTestDescription struct {
FullTestText string FullTestText string
ComponentTexts []string ComponentTexts []string
@@ -139,6 +140,8 @@ type GinkgoTestDescription struct {
FileName string FileName string
LineNumber int LineNumber int
Failed bool
} }
//CurrentGinkgoTestDescripton returns information about the current running test. //CurrentGinkgoTestDescripton returns information about the current running test.
@@ -157,6 +160,7 @@ func CurrentGinkgoTestDescription() GinkgoTestDescription {
IsMeasurement: summary.IsMeasurement, IsMeasurement: summary.IsMeasurement,
FileName: subjectCodeLocation.FileName, FileName: subjectCodeLocation.FileName,
LineNumber: subjectCodeLocation.LineNumber, LineNumber: subjectCodeLocation.LineNumber,
Failed: summary.HasFailureState(),
} }
} }
@@ -218,6 +222,17 @@ func buildDefaultReporter() Reporter {
} }
} }
//Skip notifies Ginkgo that the current spec should be skipped.
func Skip(message string, callerSkip ...int) {
skip := 0
if len(callerSkip) > 0 {
skip = callerSkip[0]
}
globalFailer.Skip(message, codelocation.New(skip+1))
panic(GINKGO_PANIC)
}
//Fail notifies Ginkgo that the current spec has failed. (Gomega will call Fail for you automatically when an assertion fails.) //Fail notifies Ginkgo that the current spec has failed. (Gomega will call Fail for you automatically when an assertion fails.)
func Fail(message string, callerSkip ...int) { func Fail(message string, callerSkip ...int) {
skip := 0 skip := 0

View File

@@ -77,3 +77,16 @@ func (f *Failer) Drain(componentType types.SpecComponentType, componentIndex int
return failure, outcome return failure, outcome
} }
func (f *Failer) Skip(message string, location types.CodeLocation) {
f.lock.Lock()
defer f.lock.Unlock()
if f.state == types.SpecStatePassed {
f.state = types.SpecStateSkipped
f.failure = types.SpecFailure{
Message: message,
Location: location,
}
}
}

View File

@@ -68,8 +68,10 @@ func (r *runner) runAsync() (outcome types.SpecState, failure types.SpecFailure)
done := make(chan interface{}, 1) done := make(chan interface{}, 1)
go func() { go func() {
finished := false
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil || !finished {
r.failer.Panic(codelocation.New(2), e) r.failer.Panic(codelocation.New(2), e)
select { select {
case <-done: case <-done:
@@ -81,6 +83,7 @@ func (r *runner) runAsync() (outcome types.SpecState, failure types.SpecFailure)
}() }()
r.asyncFunc(done) r.asyncFunc(done)
finished = true
}() }()
select { select {
@@ -93,8 +96,10 @@ func (r *runner) runAsync() (outcome types.SpecState, failure types.SpecFailure)
return return
} }
func (r *runner) runSync() (outcome types.SpecState, failure types.SpecFailure) { func (r *runner) runSync() (outcome types.SpecState, failure types.SpecFailure) {
finished := false
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil || !finished {
r.failer.Panic(codelocation.New(2), e) r.failer.Panic(codelocation.New(2), e)
} }
@@ -102,6 +107,7 @@ func (r *runner) runSync() (outcome types.SpecState, failure types.SpecFailure)
}() }()
r.syncFunc() r.syncFunc()
finished = true
return return
} }

View File

@@ -209,7 +209,7 @@ func (aggregator *Aggregator) announceSpec(specSummary *types.SpecSummary) {
case types.SpecStatePending: case types.SpecStatePending:
aggregator.stenographer.AnnouncePendingSpec(specSummary, aggregator.config.NoisyPendings && !aggregator.config.Succinct) aggregator.stenographer.AnnouncePendingSpec(specSummary, aggregator.config.NoisyPendings && !aggregator.config.Succinct)
case types.SpecStateSkipped: case types.SpecStateSkipped:
aggregator.stenographer.AnnounceSkippedSpec(specSummary) aggregator.stenographer.AnnounceSkippedSpec(specSummary, aggregator.config.Succinct, aggregator.config.FullTrace)
case types.SpecStateTimedOut: case types.SpecStateTimedOut:
aggregator.stenographer.AnnounceSpecTimedOut(specSummary, aggregator.config.Succinct, aggregator.config.FullTrace) aggregator.stenographer.AnnounceSpecTimedOut(specSummary, aggregator.config.Succinct, aggregator.config.FullTrace)
case types.SpecStatePanicked: case types.SpecStatePanicked:

View File

@@ -14,8 +14,6 @@ func NewOutputInterceptor() OutputInterceptor {
} }
type outputInterceptor struct { type outputInterceptor struct {
stdoutPlaceholder *os.File
stderrPlaceholder *os.File
redirectFile *os.File redirectFile *os.File
intercepting bool intercepting bool
} }
@@ -33,19 +31,6 @@ func (interceptor *outputInterceptor) StartInterceptingOutput() error {
return err return err
} }
interceptor.stdoutPlaceholder, err = ioutil.TempFile("", "ginkgo-output")
if err != nil {
return err
}
interceptor.stderrPlaceholder, err = ioutil.TempFile("", "ginkgo-output")
if err != nil {
return err
}
syscall.Dup2(1, int(interceptor.stdoutPlaceholder.Fd()))
syscall.Dup2(2, int(interceptor.stderrPlaceholder.Fd()))
syscall.Dup2(int(interceptor.redirectFile.Fd()), 1) syscall.Dup2(int(interceptor.redirectFile.Fd()), 1)
syscall.Dup2(int(interceptor.redirectFile.Fd()), 2) syscall.Dup2(int(interceptor.redirectFile.Fd()), 2)
@@ -57,18 +42,9 @@ func (interceptor *outputInterceptor) StopInterceptingAndReturnOutput() (string,
return "", errors.New("Not intercepting output!") return "", errors.New("Not intercepting output!")
} }
syscall.Dup2(int(interceptor.stdoutPlaceholder.Fd()), 1) interceptor.redirectFile.Close()
syscall.Dup2(int(interceptor.stderrPlaceholder.Fd()), 2)
for _, f := range []*os.File{interceptor.redirectFile, interceptor.stdoutPlaceholder, interceptor.stderrPlaceholder} {
f.Close()
}
output, err := ioutil.ReadFile(interceptor.redirectFile.Name()) output, err := ioutil.ReadFile(interceptor.redirectFile.Name())
os.Remove(interceptor.redirectFile.Name())
for _, f := range []*os.File{interceptor.redirectFile, interceptor.stdoutPlaceholder, interceptor.stderrPlaceholder} {
os.Remove(f.Name())
}
interceptor.intercepting = false interceptor.intercepting = false

View File

@@ -115,7 +115,7 @@ func (spec *Spec) Run(writer io.Writer) {
}() }()
for sample := 0; sample < spec.subject.Samples(); sample++ { for sample := 0; sample < spec.subject.Samples(); sample++ {
spec.state, spec.failure = spec.runSample(sample, writer) spec.runSample(sample, writer)
if spec.state != types.SpecStatePassed { if spec.state != types.SpecStatePassed {
return return
@@ -123,9 +123,9 @@ func (spec *Spec) Run(writer io.Writer) {
} }
} }
func (spec *Spec) runSample(sample int, writer io.Writer) (specState types.SpecState, specFailure types.SpecFailure) { func (spec *Spec) runSample(sample int, writer io.Writer) {
specState = types.SpecStatePassed spec.state = types.SpecStatePassed
specFailure = types.SpecFailure{} spec.failure = types.SpecFailure{}
innerMostContainerIndexToUnwind := -1 innerMostContainerIndexToUnwind := -1
defer func() { defer func() {
@@ -134,9 +134,9 @@ func (spec *Spec) runSample(sample int, writer io.Writer) (specState types.SpecS
for _, afterEach := range container.SetupNodesOfType(types.SpecComponentTypeAfterEach) { for _, afterEach := range container.SetupNodesOfType(types.SpecComponentTypeAfterEach) {
spec.announceSetupNode(writer, "AfterEach", container, afterEach) spec.announceSetupNode(writer, "AfterEach", container, afterEach)
afterEachState, afterEachFailure := afterEach.Run() afterEachState, afterEachFailure := afterEach.Run()
if afterEachState != types.SpecStatePassed && specState == types.SpecStatePassed { if afterEachState != types.SpecStatePassed && spec.state == types.SpecStatePassed {
specState = afterEachState spec.state = afterEachState
specFailure = afterEachFailure spec.failure = afterEachFailure
} }
} }
} }
@@ -146,8 +146,8 @@ func (spec *Spec) runSample(sample int, writer io.Writer) (specState types.SpecS
innerMostContainerIndexToUnwind = i innerMostContainerIndexToUnwind = i
for _, beforeEach := range container.SetupNodesOfType(types.SpecComponentTypeBeforeEach) { for _, beforeEach := range container.SetupNodesOfType(types.SpecComponentTypeBeforeEach) {
spec.announceSetupNode(writer, "BeforeEach", container, beforeEach) spec.announceSetupNode(writer, "BeforeEach", container, beforeEach)
specState, specFailure = beforeEach.Run() spec.state, spec.failure = beforeEach.Run()
if specState != types.SpecStatePassed { if spec.state != types.SpecStatePassed {
return return
} }
} }
@@ -156,17 +156,15 @@ func (spec *Spec) runSample(sample int, writer io.Writer) (specState types.SpecS
for _, container := range spec.containers { for _, container := range spec.containers {
for _, justBeforeEach := range container.SetupNodesOfType(types.SpecComponentTypeJustBeforeEach) { for _, justBeforeEach := range container.SetupNodesOfType(types.SpecComponentTypeJustBeforeEach) {
spec.announceSetupNode(writer, "JustBeforeEach", container, justBeforeEach) spec.announceSetupNode(writer, "JustBeforeEach", container, justBeforeEach)
specState, specFailure = justBeforeEach.Run() spec.state, spec.failure = justBeforeEach.Run()
if specState != types.SpecStatePassed { if spec.state != types.SpecStatePassed {
return return
} }
} }
} }
spec.announceSubject(writer, spec.subject) spec.announceSubject(writer, spec.subject)
specState, specFailure = spec.subject.Run() spec.state, spec.failure = spec.subject.Run()
return
} }
func (spec *Spec) announceSetupNode(writer io.Writer, nodeType string, container *containernode.ContainerNode, setupNode leafnodes.BasicNode) { func (spec *Spec) announceSetupNode(writer io.Writer, nodeType string, container *containernode.ContainerNode, setupNode leafnodes.BasicNode) {

View File

@@ -52,7 +52,7 @@ func (e *Specs) ApplyFocus(description string, focusString string, skipString st
func (e *Specs) applyProgrammaticFocus() { func (e *Specs) applyProgrammaticFocus() {
e.hasProgrammaticFocus = false e.hasProgrammaticFocus = false
for _, spec := range e.specs { for _, spec := range e.specs {
if spec.Focused() { if spec.Focused() && !spec.Pending() {
e.hasProgrammaticFocus = true e.hasProgrammaticFocus = true
break break
} }

View File

@@ -170,7 +170,7 @@ func (runner *SpecRunner) CurrentSpecSummary() (*types.SpecSummary, bool) {
func (runner *SpecRunner) registerForInterrupts() { func (runner *SpecRunner) registerForInterrupts() {
c := make(chan os.Signal, 1) c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt) signal.Notify(c, os.Interrupt, syscall.SIGTERM)
<-c <-c
signal.Stop(c) signal.Stop(c)

View File

@@ -65,7 +65,7 @@ func (reporter *DefaultReporter) SpecDidComplete(specSummary *types.SpecSummary)
case types.SpecStatePending: case types.SpecStatePending:
reporter.stenographer.AnnouncePendingSpec(specSummary, reporter.config.NoisyPendings && !reporter.config.Succinct) reporter.stenographer.AnnouncePendingSpec(specSummary, reporter.config.NoisyPendings && !reporter.config.Succinct)
case types.SpecStateSkipped: case types.SpecStateSkipped:
reporter.stenographer.AnnounceSkippedSpec(specSummary) reporter.stenographer.AnnounceSkippedSpec(specSummary, reporter.config.Succinct, reporter.config.FullTrace)
case types.SpecStateTimedOut: case types.SpecStateTimedOut:
reporter.stenographer.AnnounceSpecTimedOut(specSummary, reporter.config.Succinct, reporter.config.FullTrace) reporter.stenographer.AnnounceSpecTimedOut(specSummary, reporter.config.Succinct, reporter.config.FullTrace)
case types.SpecStatePanicked: case types.SpecStatePanicked:

View File

@@ -117,8 +117,8 @@ func (stenographer *FakeStenographer) AnnouncePendingSpec(spec *types.SpecSummar
stenographer.registerCall("AnnouncePendingSpec", spec, noisy) stenographer.registerCall("AnnouncePendingSpec", spec, noisy)
} }
func (stenographer *FakeStenographer) AnnounceSkippedSpec(spec *types.SpecSummary) { func (stenographer *FakeStenographer) AnnounceSkippedSpec(spec *types.SpecSummary, succinct bool, fullTrace bool) {
stenographer.registerCall("AnnounceSkippedSpec", spec) stenographer.registerCall("AnnounceSkippedSpec", spec, succinct, fullTrace)
} }
func (stenographer *FakeStenographer) AnnounceSpecTimedOut(spec *types.SpecSummary, succinct bool, fullTrace bool) { func (stenographer *FakeStenographer) AnnounceSpecTimedOut(spec *types.SpecSummary, succinct bool, fullTrace bool) {

View File

@@ -49,7 +49,7 @@ type Stenographer interface {
AnnounceSuccesfulMeasurement(spec *types.SpecSummary, succinct bool) AnnounceSuccesfulMeasurement(spec *types.SpecSummary, succinct bool)
AnnouncePendingSpec(spec *types.SpecSummary, noisy bool) AnnouncePendingSpec(spec *types.SpecSummary, noisy bool)
AnnounceSkippedSpec(spec *types.SpecSummary) AnnounceSkippedSpec(spec *types.SpecSummary, succinct bool, fullTrace bool)
AnnounceSpecTimedOut(spec *types.SpecSummary, succinct bool, fullTrace bool) AnnounceSpecTimedOut(spec *types.SpecSummary, succinct bool, fullTrace bool)
AnnounceSpecPanicked(spec *types.SpecSummary, succinct bool, fullTrace bool) AnnounceSpecPanicked(spec *types.SpecSummary, succinct bool, fullTrace bool)
@@ -197,7 +197,7 @@ func (s *consoleStenographer) announceSetupFailure(name string, summary *types.S
s.println(0, s.colorize(redColor+boldStyle, "%s [%.3f seconds]", message, summary.RunTime.Seconds())) s.println(0, s.colorize(redColor+boldStyle, "%s [%.3f seconds]", message, summary.RunTime.Seconds()))
indentation := s.printCodeLocationBlock([]string{name}, []types.CodeLocation{summary.CodeLocation}, summary.ComponentType, 0, true, true) indentation := s.printCodeLocationBlock([]string{name}, []types.CodeLocation{summary.CodeLocation}, summary.ComponentType, 0, summary.State, true)
s.printNewLine() s.printNewLine()
s.printFailure(indentation, summary.State, summary.Failure, fullTrace) s.printFailure(indentation, summary.State, summary.Failure, fullTrace)
@@ -252,9 +252,21 @@ func (s *consoleStenographer) AnnouncePendingSpec(spec *types.SpecSummary, noisy
} }
} }
func (s *consoleStenographer) AnnounceSkippedSpec(spec *types.SpecSummary) { func (s *consoleStenographer) AnnounceSkippedSpec(spec *types.SpecSummary, succinct bool, fullTrace bool) {
// Skips at runtime will have a non-empty spec.Failure. All others should be succinct.
if succinct || spec.Failure == (types.SpecFailure{}) {
s.print(0, s.colorize(cyanColor, "S")) s.print(0, s.colorize(cyanColor, "S"))
s.stream() s.stream()
} else {
s.startBlock()
s.println(0, s.colorize(cyanColor+boldStyle, "S [SKIPPING]%s [%.3f seconds]", s.failureContext(spec.Failure.ComponentType), spec.RunTime.Seconds()))
indentation := s.printCodeLocationBlock(spec.ComponentTexts, spec.ComponentCodeLocations, spec.Failure.ComponentType, spec.Failure.ComponentIndex, spec.State, succinct)
s.printNewLine()
s.printSkip(indentation, spec.Failure)
s.endBlock()
}
} }
func (s *consoleStenographer) AnnounceSpecTimedOut(spec *types.SpecSummary, succinct bool, fullTrace bool) { func (s *consoleStenographer) AnnounceSpecTimedOut(spec *types.SpecSummary, succinct bool, fullTrace bool) {
@@ -299,7 +311,7 @@ func (s *consoleStenographer) SummarizeFailures(summaries []*types.SpecSummary)
} else if summary.Failed() { } else if summary.Failed() {
s.print(0, s.colorize(redColor+boldStyle, "[Fail] ")) s.print(0, s.colorize(redColor+boldStyle, "[Fail] "))
} }
s.printSpecContext(summary.ComponentTexts, summary.ComponentCodeLocations, summary.Failure.ComponentType, summary.Failure.ComponentIndex, true, true) s.printSpecContext(summary.ComponentTexts, summary.ComponentCodeLocations, summary.Failure.ComponentType, summary.Failure.ComponentIndex, summary.State, true)
s.printNewLine() s.printNewLine()
s.println(0, s.colorize(lightGrayColor, summary.Failure.Location.String())) s.println(0, s.colorize(lightGrayColor, summary.Failure.Location.String()))
} }
@@ -332,7 +344,7 @@ func (s *consoleStenographer) printBlockWithMessage(header string, message strin
s.startBlock() s.startBlock()
s.println(0, header) s.println(0, header)
indentation := s.printCodeLocationBlock(spec.ComponentTexts, spec.ComponentCodeLocations, types.SpecComponentTypeInvalid, 0, false, succinct) indentation := s.printCodeLocationBlock(spec.ComponentTexts, spec.ComponentCodeLocations, types.SpecComponentTypeInvalid, 0, spec.State, succinct)
if message != "" { if message != "" {
s.printNewLine() s.printNewLine()
@@ -346,7 +358,7 @@ func (s *consoleStenographer) printSpecFailure(message string, spec *types.SpecS
s.startBlock() s.startBlock()
s.println(0, s.colorize(redColor+boldStyle, "%s%s [%.3f seconds]", message, s.failureContext(spec.Failure.ComponentType), spec.RunTime.Seconds())) s.println(0, s.colorize(redColor+boldStyle, "%s%s [%.3f seconds]", message, s.failureContext(spec.Failure.ComponentType), spec.RunTime.Seconds()))
indentation := s.printCodeLocationBlock(spec.ComponentTexts, spec.ComponentCodeLocations, spec.Failure.ComponentType, spec.Failure.ComponentIndex, true, succinct) indentation := s.printCodeLocationBlock(spec.ComponentTexts, spec.ComponentCodeLocations, spec.Failure.ComponentType, spec.Failure.ComponentIndex, spec.State, succinct)
s.printNewLine() s.printNewLine()
s.printFailure(indentation, spec.State, spec.Failure, fullTrace) s.printFailure(indentation, spec.State, spec.Failure, fullTrace)
@@ -370,6 +382,12 @@ func (s *consoleStenographer) failureContext(failedComponentType types.SpecCompo
return "" return ""
} }
func (s *consoleStenographer) printSkip(indentation int, spec types.SpecFailure) {
s.println(indentation, s.colorize(cyanColor, spec.Message))
s.printNewLine()
s.println(indentation, spec.Location.String())
}
func (s *consoleStenographer) printFailure(indentation int, state types.SpecState, failure types.SpecFailure, fullTrace bool) { func (s *consoleStenographer) printFailure(indentation int, state types.SpecState, failure types.SpecFailure, fullTrace bool) {
if state == types.SpecStatePanicked { if state == types.SpecStatePanicked {
s.println(indentation, s.colorize(redColor+boldStyle, failure.Message)) s.println(indentation, s.colorize(redColor+boldStyle, failure.Message))
@@ -390,7 +408,7 @@ func (s *consoleStenographer) printFailure(indentation int, state types.SpecStat
} }
} }
func (s *consoleStenographer) printSpecContext(componentTexts []string, componentCodeLocations []types.CodeLocation, failedComponentType types.SpecComponentType, failedComponentIndex int, failure bool, succinct bool) int { func (s *consoleStenographer) printSpecContext(componentTexts []string, componentCodeLocations []types.CodeLocation, failedComponentType types.SpecComponentType, failedComponentIndex int, state types.SpecState, succinct bool) int {
startIndex := 1 startIndex := 1
indentation := 0 indentation := 0
@@ -399,7 +417,11 @@ func (s *consoleStenographer) printSpecContext(componentTexts []string, componen
} }
for i := startIndex; i < len(componentTexts); i++ { for i := startIndex; i < len(componentTexts); i++ {
if failure && i == failedComponentIndex { if (state.IsFailure() || state == types.SpecStateSkipped) && i == failedComponentIndex {
color := redColor
if state == types.SpecStateSkipped {
color = cyanColor
}
blockType := "" blockType := ""
switch failedComponentType { switch failedComponentType {
case types.SpecComponentTypeBeforeSuite: case types.SpecComponentTypeBeforeSuite:
@@ -418,9 +440,9 @@ func (s *consoleStenographer) printSpecContext(componentTexts []string, componen
blockType = "Measurement" blockType = "Measurement"
} }
if succinct { if succinct {
s.print(0, s.colorize(redColor+boldStyle, "[%s] %s ", blockType, componentTexts[i])) s.print(0, s.colorize(color+boldStyle, "[%s] %s ", blockType, componentTexts[i]))
} else { } else {
s.println(indentation, s.colorize(redColor+boldStyle, "%s [%s]", componentTexts[i], blockType)) s.println(indentation, s.colorize(color+boldStyle, "%s [%s]", componentTexts[i], blockType))
s.println(indentation, s.colorize(grayColor, "%s", componentCodeLocations[i])) s.println(indentation, s.colorize(grayColor, "%s", componentCodeLocations[i]))
} }
} else { } else {
@@ -437,8 +459,8 @@ func (s *consoleStenographer) printSpecContext(componentTexts []string, componen
return indentation return indentation
} }
func (s *consoleStenographer) printCodeLocationBlock(componentTexts []string, componentCodeLocations []types.CodeLocation, failedComponentType types.SpecComponentType, failedComponentIndex int, failure bool, succinct bool) int { func (s *consoleStenographer) printCodeLocationBlock(componentTexts []string, componentCodeLocations []types.CodeLocation, failedComponentType types.SpecComponentType, failedComponentIndex int, state types.SpecState, succinct bool) int {
indentation := s.printSpecContext(componentTexts, componentCodeLocations, failedComponentType, failedComponentIndex, failure, succinct) indentation := s.printSpecContext(componentTexts, componentCodeLocations, failedComponentType, failedComponentIndex, state, succinct)
if succinct { if succinct {
if len(componentTexts) > 0 { if len(componentTexts) > 0 {

View File

@@ -35,7 +35,7 @@ type SpecSummary struct {
} }
func (s SpecSummary) HasFailureState() bool { func (s SpecSummary) HasFailureState() bool {
return s.State == SpecStateTimedOut || s.State == SpecStatePanicked || s.State == SpecStateFailed return s.State.IsFailure()
} }
func (s SpecSummary) TimedOut() bool { func (s SpecSummary) TimedOut() bool {
@@ -115,6 +115,10 @@ const (
SpecStateTimedOut SpecStateTimedOut
) )
func (state SpecState) IsFailure() bool {
return state == SpecStateTimedOut || state == SpecStatePanicked || state == SpecStateFailed
}
type SpecComponentType uint type SpecComponentType uint
const ( const (

View File

@@ -0,0 +1,340 @@
Copyright (c) 2013 The go-github AUTHORS. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------
Some documentation is taken from the GitHub Developer site
<http://developer.github.com/>, which is available under a Creative Commons
Attribution 3.0 License:
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
CONDITIONS.
1. Definitions
a. "Adaptation" means a work based upon the Work, or upon the Work and
other pre-existing works, such as a translation, adaptation,
derivative work, arrangement of music or other alterations of a
literary or artistic work, or phonogram or performance and includes
cinematographic adaptations or any other form in which the Work may be
recast, transformed, or adapted including in any form recognizably
derived from the original, except that a work that constitutes a
Collection will not be considered an Adaptation for the purpose of
this License. For the avoidance of doubt, where the Work is a musical
work, performance or phonogram, the synchronization of the Work in
timed-relation with a moving image ("synching") will be considered an
Adaptation for the purpose of this License.
b. "Collection" means a collection of literary or artistic works, such as
encyclopedias and anthologies, or performances, phonograms or
broadcasts, or other works or subject matter other than works listed
in Section 1(f) below, which, by reason of the selection and
arrangement of their contents, constitute intellectual creations, in
which the Work is included in its entirety in unmodified form along
with one or more other contributions, each constituting separate and
independent works in themselves, which together are assembled into a
collective whole. A work that constitutes a Collection will not be
considered an Adaptation (as defined above) for the purposes of this
License.
c. "Distribute" means to make available to the public the original and
copies of the Work or Adaptation, as appropriate, through sale or
other transfer of ownership.
d. "Licensor" means the individual, individuals, entity or entities that
offer(s) the Work under the terms of this License.
e. "Original Author" means, in the case of a literary or artistic work,
the individual, individuals, entity or entities who created the Work
or if no individual or entity can be identified, the publisher; and in
addition (i) in the case of a performance the actors, singers,
musicians, dancers, and other persons who act, sing, deliver, declaim,
play in, interpret or otherwise perform literary or artistic works or
expressions of folklore; (ii) in the case of a phonogram the producer
being the person or legal entity who first fixes the sounds of a
performance or other sounds; and, (iii) in the case of broadcasts, the
organization that transmits the broadcast.
f. "Work" means the literary and/or artistic work offered under the terms
of this License including without limitation any production in the
literary, scientific and artistic domain, whatever may be the mode or
form of its expression including digital form, such as a book,
pamphlet and other writing; a lecture, address, sermon or other work
of the same nature; a dramatic or dramatico-musical work; a
choreographic work or entertainment in dumb show; a musical
composition with or without words; a cinematographic work to which are
assimilated works expressed by a process analogous to cinematography;
a work of drawing, painting, architecture, sculpture, engraving or
lithography; a photographic work to which are assimilated works
expressed by a process analogous to photography; a work of applied
art; an illustration, map, plan, sketch or three-dimensional work
relative to geography, topography, architecture or science; a
performance; a broadcast; a phonogram; a compilation of data to the
extent it is protected as a copyrightable work; or a work performed by
a variety or circus performer to the extent it is not otherwise
considered a literary or artistic work.
g. "You" means an individual or entity exercising rights under this
License who has not previously violated the terms of this License with
respect to the Work, or who has received express permission from the
Licensor to exercise rights under this License despite a previous
violation.
h. "Publicly Perform" means to perform public recitations of the Work and
to communicate to the public those public recitations, by any means or
process, including by wire or wireless means or public digital
performances; to make available to the public Works in such a way that
members of the public may access these Works from a place and at a
place individually chosen by them; to perform the Work to the public
by any means or process and the communication to the public of the
performances of the Work, including by public digital performance; to
broadcast and rebroadcast the Work by any means including signs,
sounds or images.
i. "Reproduce" means to make copies of the Work by any means including
without limitation by sound or visual recordings and the right of
fixation and reproducing fixations of the Work, including storage of a
protected performance or phonogram in digital form or other electronic
medium.
2. Fair Dealing Rights. Nothing in this License is intended to reduce,
limit, or restrict any uses free from copyright or rights arising from
limitations or exceptions that are provided for in connection with the
copyright protection under copyright law or other applicable laws.
3. License Grant. Subject to the terms and conditions of this License,
Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
perpetual (for the duration of the applicable copyright) license to
exercise the rights in the Work as stated below:
a. to Reproduce the Work, to incorporate the Work into one or more
Collections, and to Reproduce the Work as incorporated in the
Collections;
b. to create and Reproduce Adaptations provided that any such Adaptation,
including any translation in any medium, takes reasonable steps to
clearly label, demarcate or otherwise identify that changes were made
to the original Work. For example, a translation could be marked "The
original work was translated from English to Spanish," or a
modification could indicate "The original work has been modified.";
c. to Distribute and Publicly Perform the Work including as incorporated
in Collections; and,
d. to Distribute and Publicly Perform Adaptations.
e. For the avoidance of doubt:
i. Non-waivable Compulsory License Schemes. In those jurisdictions in
which the right to collect royalties through any statutory or
compulsory licensing scheme cannot be waived, the Licensor
reserves the exclusive right to collect such royalties for any
exercise by You of the rights granted under this License;
ii. Waivable Compulsory License Schemes. In those jurisdictions in
which the right to collect royalties through any statutory or
compulsory licensing scheme can be waived, the Licensor waives the
exclusive right to collect such royalties for any exercise by You
of the rights granted under this License; and,
iii. Voluntary License Schemes. The Licensor waives the right to
collect royalties, whether individually or, in the event that the
Licensor is a member of a collecting society that administers
voluntary licensing schemes, via that society, from any exercise
by You of the rights granted under this License.
The above rights may be exercised in all media and formats whether now
known or hereafter devised. The above rights include the right to make
such modifications as are technically necessary to exercise the rights in
other media and formats. Subject to Section 8(f), all rights not expressly
granted by Licensor are hereby reserved.
4. Restrictions. The license granted in Section 3 above is expressly made
subject to and limited by the following restrictions:
a. You may Distribute or Publicly Perform the Work only under the terms
of this License. You must include a copy of, or the Uniform Resource
Identifier (URI) for, this License with every copy of the Work You
Distribute or Publicly Perform. You may not offer or impose any terms
on the Work that restrict the terms of this License or the ability of
the recipient of the Work to exercise the rights granted to that
recipient under the terms of the License. You may not sublicense the
Work. You must keep intact all notices that refer to this License and
to the disclaimer of warranties with every copy of the Work You
Distribute or Publicly Perform. When You Distribute or Publicly
Perform the Work, You may not impose any effective technological
measures on the Work that restrict the ability of a recipient of the
Work from You to exercise the rights granted to that recipient under
the terms of the License. This Section 4(a) applies to the Work as
incorporated in a Collection, but this does not require the Collection
apart from the Work itself to be made subject to the terms of this
License. If You create a Collection, upon notice from any Licensor You
must, to the extent practicable, remove from the Collection any credit
as required by Section 4(b), as requested. If You create an
Adaptation, upon notice from any Licensor You must, to the extent
practicable, remove from the Adaptation any credit as required by
Section 4(b), as requested.
b. If You Distribute, or Publicly Perform the Work or any Adaptations or
Collections, You must, unless a request has been made pursuant to
Section 4(a), keep intact all copyright notices for the Work and
provide, reasonable to the medium or means You are utilizing: (i) the
name of the Original Author (or pseudonym, if applicable) if supplied,
and/or if the Original Author and/or Licensor designate another party
or parties (e.g., a sponsor institute, publishing entity, journal) for
attribution ("Attribution Parties") in Licensor's copyright notice,
terms of service or by other reasonable means, the name of such party
or parties; (ii) the title of the Work if supplied; (iii) to the
extent reasonably practicable, the URI, if any, that Licensor
specifies to be associated with the Work, unless such URI does not
refer to the copyright notice or licensing information for the Work;
and (iv) , consistent with Section 3(b), in the case of an Adaptation,
a credit identifying the use of the Work in the Adaptation (e.g.,
"French translation of the Work by Original Author," or "Screenplay
based on original Work by Original Author"). The credit required by
this Section 4 (b) may be implemented in any reasonable manner;
provided, however, that in the case of a Adaptation or Collection, at
a minimum such credit will appear, if a credit for all contributing
authors of the Adaptation or Collection appears, then as part of these
credits and in a manner at least as prominent as the credits for the
other contributing authors. For the avoidance of doubt, You may only
use the credit required by this Section for the purpose of attribution
in the manner set out above and, by exercising Your rights under this
License, You may not implicitly or explicitly assert or imply any
connection with, sponsorship or endorsement by the Original Author,
Licensor and/or Attribution Parties, as appropriate, of You or Your
use of the Work, without the separate, express prior written
permission of the Original Author, Licensor and/or Attribution
Parties.
c. Except as otherwise agreed in writing by the Licensor or as may be
otherwise permitted by applicable law, if You Reproduce, Distribute or
Publicly Perform the Work either by itself or as part of any
Adaptations or Collections, You must not distort, mutilate, modify or
take other derogatory action in relation to the Work which would be
prejudicial to the Original Author's honor or reputation. Licensor
agrees that in those jurisdictions (e.g. Japan), in which any exercise
of the right granted in Section 3(b) of this License (the right to
make Adaptations) would be deemed to be a distortion, mutilation,
modification or other derogatory action prejudicial to the Original
Author's honor and reputation, the Licensor will waive or not assert,
as appropriate, this Section, to the fullest extent permitted by the
applicable national law, to enable You to reasonably exercise Your
right under Section 3(b) of this License (right to make Adaptations)
but not otherwise.
5. Representations, Warranties and Disclaimer
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. Termination
a. This License and the rights granted hereunder will terminate
automatically upon any breach by You of the terms of this License.
Individuals or entities who have received Adaptations or Collections
from You under this License, however, will not have their licenses
terminated provided such individuals or entities remain in full
compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
survive any termination of this License.
b. Subject to the above terms and conditions, the license granted here is
perpetual (for the duration of the applicable copyright in the Work).
Notwithstanding the above, Licensor reserves the right to release the
Work under different license terms or to stop distributing the Work at
any time; provided, however that any such election will not serve to
withdraw this License (or any other license that has been, or is
required to be, granted under the terms of this License), and this
License will continue in full force and effect unless terminated as
stated above.
8. Miscellaneous
a. Each time You Distribute or Publicly Perform the Work or a Collection,
the Licensor offers to the recipient a license to the Work on the same
terms and conditions as the license granted to You under this License.
b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
offers to the recipient a license to the original Work on the same
terms and conditions as the license granted to You under this License.
c. If any provision of this License is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this License, and without further action
by the parties to this agreement, such provision shall be reformed to
the minimum extent necessary to make such provision valid and
enforceable.
d. No term or provision of this License shall be deemed waived and no
breach consented to unless such waiver or consent shall be in writing
and signed by the party to be charged with such waiver or consent.
e. This License constitutes the entire agreement between the parties with
respect to the Work licensed here. There are no understandings,
agreements or representations with respect to the Work not specified
here. Licensor shall not be bound by any additional provisions that
may appear in any communication from You. This License may not be
modified without the mutual written agreement of the Licensor and You.
f. The rights granted under, and the subject matter referenced, in this
License were drafted utilizing the terminology of the Berne Convention
for the Protection of Literary and Artistic Works (as amended on
September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
and the Universal Copyright Convention (as revised on July 24, 1971).
These rights and subject matter take effect in the relevant
jurisdiction in which the License terms are sought to be enforced
according to the corresponding provisions of the implementation of
those treaty provisions in the applicable national law. If the
standard suite of rights granted under applicable copyright law
includes additional rights not granted under this License, such
additional rights are deemed to be included in the License; this
License is not intended to restrict the license of any rights under
applicable law.
Creative Commons Notice
Creative Commons is not a party to this License, and makes no warranty
whatsoever in connection with the Work. Creative Commons will not be
liable to You or any party on any legal theory for any damages
whatsoever, including without limitation any general, special,
incidental or consequential damages arising in connection to this
license. Notwithstanding the foregoing two (2) sentences, if Creative
Commons has expressly identified itself as the Licensor hereunder, it
shall have all rights and obligations of Licensor.
Except for the limited purpose of indicating to the public that the
Work is licensed under the CCPL, Creative Commons does not authorize
the use by either party of the trademark "Creative Commons" or any
related trademark or logo of Creative Commons without the prior
written consent of Creative Commons. Any permitted use will be in
compliance with Creative Commons' then-current trademark usage
guidelines, as may be published on its website or otherwise made
available upon request from time to time. For the avoidance of doubt,
this trademark restriction does not form part of this License.
Creative Commons may be contacted at http://creativecommons.org/.