bump(github.com/onsi/gingko):bb93381d543b0e5725244abe752214a110791d01

Adds support for extracting stdout and stderr from failed tests runs
into junit, which makes junit more effective for debugging failures.
pull/6/head
Clayton Coleman 2017-02-22 23:58:02 -05:00
parent 452420484c
commit 4e24e82b13
No known key found for this signature in database
GPG Key ID: 3D16906B4F1C5CB3
35 changed files with 1396 additions and 131 deletions

98
Godeps/Godeps.json generated
View File

@ -1751,113 +1751,123 @@
},
{
"ImportPath": "github.com/onsi/ginkgo",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/config",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/ginkgo",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/ginkgo/convert",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/ginkgo/interrupthandler",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/ginkgo/nodot",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/ginkgo/testrunner",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/ginkgo/testsuite",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/ginkgo/watch",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/codelocation",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/containernode",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/failer",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/leafnodes",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/remote",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/spec",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/specrunner",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/suite",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/testingtproxy",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/writer",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/reporters",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/reporters/stenographer",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable",
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty",
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/ginkgo/types",
"Comment": "v1.2.0-66-g74c678d",
"Rev": "74c678d97c305753605c338c6c78c49ec104b5e7"
"Comment": "v1.2.0-90-gbb93381",
"Rev": "bb93381d543b0e5725244abe752214a110791d01"
},
{
"ImportPath": "github.com/onsi/gomega",

56
Godeps/LICENSES generated
View File

@ -60964,6 +60964,62 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================================================
================================================================================
= vendor/github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable licensed under: =
Copyright (c) 2013-2014 Onsi Fakhouri
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.
= vendor/github.com/onsi/ginkgo/LICENSE 570603114d52313cb86c0206401c9af7 -
================================================================================
================================================================================
= vendor/github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty licensed under: =
Copyright (c) 2013-2014 Onsi Fakhouri
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.
= vendor/github.com/onsi/ginkgo/LICENSE 570603114d52313cb86c0206401c9af7 -
================================================================================
================================================================================
= vendor/github.com/onsi/ginkgo/types licensed under: =

View File

@ -2,7 +2,7 @@ language: go
go:
- 1.5
- 1.6
- tip
- 1.7
install:
- go get -v -t ./...

View File

@ -4,6 +4,7 @@ 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)
- Add `GinkgoRandomSeed()` - shorthand for `config.GinkgoConfig.RandomSeed`
Bug Fixes:
@ -68,7 +69,7 @@ No changes, just dropping the beta.
New Features:
- `ginkgo watch` now monitors packages *and their dependencies* for changes. The depth of the dependency tree can be modified with the `-depth` flag.
- Test suites with a programmatic focus (`FIt`, `FDescribe`, etc...) exit with non-zero status code, evne when they pass. This allows CI systems to detect accidental commits of focused test suites.
- Test suites with a programmatic focus (`FIt`, `FDescribe`, etc...) exit with non-zero status code, even when they pass. This allows CI systems to detect accidental commits of focused test suites.
- `ginkgo -p` runs the testsuite in parallel with an auto-detected number of nodes.
- `ginkgo -tags=TAG_LIST` passes a list of tags down to the `go build` command.
- `ginkgo --failFast` aborts the test suite after the first failure.

View File

@ -4,6 +4,7 @@ import (
"bytes"
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
@ -15,11 +16,15 @@ import (
)
func BuildBootstrapCommand() *Command {
var agouti, noDot, internal bool
var (
agouti, noDot, internal bool
customBootstrapFile string
)
flagSet := flag.NewFlagSet("bootstrap", flag.ExitOnError)
flagSet.BoolVar(&agouti, "agouti", false, "If set, bootstrap will generate a bootstrap file for writing Agouti tests")
flagSet.BoolVar(&noDot, "nodot", false, "If set, bootstrap will generate a bootstrap file that does not . import ginkgo and gomega")
flagSet.BoolVar(&internal, "internal", false, "If set, generate will generate a test file that uses the regular package name")
flagSet.StringVar(&customBootstrapFile, "template", "", "If specified, generate will use the contents of the file passed as the bootstrap template")
return &Command{
Name: "bootstrap",
@ -30,7 +35,7 @@ func BuildBootstrapCommand() *Command {
"Accepts the following flags:",
},
Command: func(args []string, additionalArgs []string) {
generateBootstrap(agouti, noDot, internal)
generateBootstrap(agouti, noDot, internal, customBootstrapFile)
},
}
}
@ -132,7 +137,7 @@ func fileExists(path string) bool {
return false
}
func generateBootstrap(agouti, noDot, internal bool) {
func generateBootstrap(agouti, noDot, internal bool, customBootstrapFile string) {
packageName, bootstrapFilePrefix, formattedName := getPackageAndFormattedName()
data := bootstrapData{
Package: determinePackageName(packageName, internal),
@ -162,7 +167,13 @@ func generateBootstrap(agouti, noDot, internal bool) {
defer f.Close()
var templateText string
if agouti {
if customBootstrapFile != "" {
tpl, err := ioutil.ReadFile(customBootstrapFile)
if err != nil {
panic(err.Error())
}
templateText = string(tpl)
} else if agouti {
templateText = agoutiBootstrapText
} else {
templateText = bootstrapText

View File

@ -46,7 +46,7 @@ func (r *SpecBuilder) BuildSpecs(args []string, additionalArgs []string) {
passed := true
for _, suite := range suites {
runner := testrunner.New(suite, 1, false, r.commandFlags.Race, r.commandFlags.Cover, r.commandFlags.CoverPkg, r.commandFlags.Tags, r.commandFlags.GCFlags, nil)
runner := testrunner.New(suite, 1, false, r.commandFlags.GoOpts, nil)
fmt.Printf("Compiling %s...\n", suite.PackageName)
path, _ := filepath.Abs(filepath.Join(suite.Path, fmt.Sprintf("%s.test", suite.PackageName)))

View File

@ -34,7 +34,7 @@ func BuildGenerateCommand() *Command {
var specText = `package {{.Package}}
import (
. "{{.PackageImportPath}}"
{{if .DotImportPackage}}. "{{.PackageImportPath}}"{{end}}
{{if .IncludeImports}}. "github.com/onsi/ginkgo"{{end}}
{{if .IncludeImports}}. "github.com/onsi/gomega"{{end}}
@ -48,7 +48,7 @@ var _ = Describe("{{.Subject}}", func() {
var agoutiSpecText = `package {{.Package}}_test
import (
. "{{.PackageImportPath}}"
{{if .DotImportPackage}}. "{{.PackageImportPath}}"{{end}}
{{if .IncludeImports}}. "github.com/onsi/ginkgo"{{end}}
{{if .IncludeImports}}. "github.com/onsi/gomega"{{end}}
@ -76,6 +76,7 @@ type specData struct {
Subject string
PackageImportPath string
IncludeImports bool
DotImportPackage bool
}
func generateSpec(args []string, agouti, noDot, internal bool) {
@ -118,6 +119,7 @@ func generateSpecForSubject(subject string, agouti, noDot, internal bool) error
Subject: formattedName,
PackageImportPath: getPackageImportPath(),
IncludeImports: !noDot,
DotImportPackage: !internal,
}
targetFile := fmt.Sprintf("%s_test.go", specFilePrefix)

View File

@ -234,7 +234,7 @@ func complainAndQuit(complaint string) {
os.Exit(1)
}
func findSuites(args []string, recurse bool, skipPackage string, allowPrecompiled bool) ([]testsuite.TestSuite, []string) {
func findSuites(args []string, recurseForAll bool, skipPackage string, allowPrecompiled bool) ([]testsuite.TestSuite, []string) {
suites := []testsuite.TestSuite{}
if len(args) > 0 {
@ -246,10 +246,15 @@ func findSuites(args []string, recurse bool, skipPackage string, allowPrecompile
continue
}
}
suites = append(suites, testsuite.SuitesInDir(arg, recurse)...)
recurseForSuite := recurseForAll
if strings.HasSuffix(arg, "/...") && arg != "/..." {
arg = arg[:len(arg)-4]
recurseForSuite = true
}
suites = append(suites, testsuite.SuitesInDir(arg, recurseForSuite)...)
}
} else {
suites = testsuite.SuitesInDir(".", recurse)
suites = testsuite.SuitesInDir(".", recurseForAll)
}
skippedPackages := []string{}

View File

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

View File

@ -9,12 +9,8 @@ import (
type RunWatchAndBuildCommandFlags struct {
Recurse bool
Race bool
Cover bool
CoverPkg string
SkipPackage string
Tags string
GCFlags string
GoOpts map[string]interface{}
//for run and watch commands
NumCPU int
@ -88,16 +84,57 @@ func (c *RunWatchAndBuildCommandFlags) computeNodes() {
}
}
func (c *RunWatchAndBuildCommandFlags) stringSlot(slot string) *string {
var opt string
c.GoOpts[slot] = &opt
return &opt
}
func (c *RunWatchAndBuildCommandFlags) boolSlot(slot string) *bool {
var opt bool
c.GoOpts[slot] = &opt
return &opt
}
func (c *RunWatchAndBuildCommandFlags) intSlot(slot string) *int {
var opt int
c.GoOpts[slot] = &opt
return &opt
}
func (c *RunWatchAndBuildCommandFlags) flags(mode int) {
c.GoOpts = make(map[string]interface{})
onWindows := (runtime.GOOS == "windows")
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.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.BoolVar(&(c.Recurse), "r", false, "Find and run test suites under the current directory recursively.")
c.FlagSet.BoolVar(c.boolSlot("race"), "race", false, "Run tests with race detection enabled.")
c.FlagSet.BoolVar(c.boolSlot("cover"), "cover", false, "Run tests with coverage analysis, will generate coverage profiles with the package name in the current directory.")
c.FlagSet.StringVar(c.stringSlot("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.Tags), "tags", "", "A list of build tags to consider satisfied during the build")
c.FlagSet.StringVar(&(c.GCFlags), "gcflags", "", "Arguments to pass on each go tool compile invocation.")
c.FlagSet.StringVar(c.stringSlot("tags"), "tags", "", "A list of build tags to consider satisfied during the build.")
c.FlagSet.StringVar(c.stringSlot("gcflags"), "gcflags", "", "Arguments to pass on each go tool compile invocation.")
c.FlagSet.StringVar(c.stringSlot("covermode"), "covermode", "", "Set the mode for coverage analysis.")
c.FlagSet.BoolVar(c.boolSlot("a"), "a", false, "Force rebuilding of packages that are already up-to-date.")
c.FlagSet.BoolVar(c.boolSlot("n"), "n", false, "Have `go test` print the commands but do not run them.")
c.FlagSet.BoolVar(c.boolSlot("msan"), "msan", false, "Enable interoperation with memory sanitizer.")
c.FlagSet.BoolVar(c.boolSlot("x"), "x", false, "Have `go test` print the commands.")
c.FlagSet.BoolVar(c.boolSlot("work"), "work", false, "Print the name of the temporary work directory and do not delete it when exiting.")
c.FlagSet.StringVar(c.stringSlot("asmflags"), "asmflags", "", "Arguments to pass on each go tool asm invocation.")
c.FlagSet.StringVar(c.stringSlot("buildmode"), "buildmode", "", "Build mode to use. See 'go help buildmode' for more.")
c.FlagSet.StringVar(c.stringSlot("compiler"), "compiler", "", "Name of compiler to use, as in runtime.Compiler (gccgo or gc).")
c.FlagSet.StringVar(c.stringSlot("gccgoflags"), "gccgoflags", "", "Arguments to pass on each gccgo compiler/linker invocation.")
c.FlagSet.StringVar(c.stringSlot("installsuffix"), "installsuffix", "", "A suffix to use in the name of the package installation directory.")
c.FlagSet.StringVar(c.stringSlot("ldflags"), "ldflags", "", "Arguments to pass on each go tool link invocation.")
c.FlagSet.BoolVar(c.boolSlot("linkshared"), "linkshared", false, "Link against shared libraries previously created with -buildmode=shared.")
c.FlagSet.StringVar(c.stringSlot("pkgdir"), "pkgdir", "", "install and load all packages from the given dir instead of the usual locations.")
c.FlagSet.StringVar(c.stringSlot("toolexec"), "toolexec", "", "a program to use to invoke toolchain programs like vet and asm.")
c.FlagSet.IntVar(c.intSlot("blockprofilerate"), "blockprofilerate", 1, "Control the detail provided in goroutine blocking profiles by calling runtime.SetBlockProfileRate with the given value.")
c.FlagSet.StringVar(c.stringSlot("coverprofile"), "coverprofile", "", "Write a coverage profile to the specified file after all tests have passed.")
c.FlagSet.StringVar(c.stringSlot("cpuprofile"), "cpuprofile", "", "Write a CPU profile to the specified file before exiting.")
c.FlagSet.StringVar(c.stringSlot("memprofile"), "memprofile", "", "Write a memory profile to the specified file after all tests have passed.")
c.FlagSet.IntVar(c.intSlot("memprofilerate"), "memprofilerate", 0, "Enable more precise (and expensive) memory profiles by setting runtime.MemProfileRate.")
c.FlagSet.StringVar(c.stringSlot("outputdir"), "outputdir", "", "Place output files from profiling in the specified directory.")
if mode == runMode || mode == watchMode {
config.Flags(c.FlagSet, "", false)

View File

@ -9,6 +9,7 @@ import (
"github.com/onsi/ginkgo/ginkgo/interrupthandler"
"github.com/onsi/ginkgo/ginkgo/testrunner"
"github.com/onsi/ginkgo/ginkgo/testsuite"
colorable "github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable"
)
type compilationInput struct {
@ -166,7 +167,7 @@ func (r *SuiteRunner) listFailedSuites(suitesThatFailed []testsuite.TestSuite) {
if config.DefaultReporterConfig.NoColor {
fmt.Printf("\t"+packageNameFormatter+" %s\n", suite.PackageName, suite.Path)
} else {
fmt.Printf("\t%s"+packageNameFormatter+"%s %s%s%s\n", redColor, suite.PackageName, defaultStyle, lightGrayColor, suite.Path, defaultStyle)
fmt.Fprintf(colorable.NewColorableStdout(), "\t%s"+packageNameFormatter+"%s %s%s%s\n", redColor, suite.PackageName, defaultStyle, lightGrayColor, suite.Path, defaultStyle)
}
}
}

View File

@ -29,25 +29,17 @@ type TestRunner struct {
numCPU int
parallelStream bool
race bool
cover bool
coverPkg string
tags string
gcFlags string
goOpts map[string]interface{}
additionalArgs []string
}
func New(suite testsuite.TestSuite, numCPU int, parallelStream bool, race bool, cover bool, coverPkg string, tags string, gcFlags string, additionalArgs []string) *TestRunner {
func New(suite testsuite.TestSuite, numCPU int, parallelStream bool, goOpts map[string]interface{}, additionalArgs []string) *TestRunner {
runner := &TestRunner{
Suite: suite,
numCPU: numCPU,
parallelStream: parallelStream,
race: race,
cover: cover,
coverPkg: coverPkg,
tags: tags,
goOpts: goOpts,
additionalArgs: additionalArgs,
gcFlags: gcFlags,
}
if !suite.Precompiled {
@ -65,6 +57,70 @@ func (t *TestRunner) Compile() error {
return t.CompileTo(t.compilationTargetPath)
}
func (t *TestRunner) BuildArgs(path string) []string {
args := []string{"test", "-c", "-i", "-o", path, t.Suite.Path}
if *t.goOpts["covermode"].(*string) != "" {
args = append(args, "-cover", fmt.Sprintf("-covermode=%s", *t.goOpts["covermode"].(*string)))
} else {
if *t.goOpts["cover"].(*bool) || *t.goOpts["coverpkg"].(*string) != "" {
args = append(args, "-cover", "-covermode=atomic")
}
}
boolOpts := []string{
"a",
"n",
"msan",
"race",
"x",
"work",
"linkshared",
}
for _, opt := range boolOpts {
if s, found := t.goOpts[opt].(*bool); found && *s {
args = append(args, fmt.Sprintf("-%s", opt))
}
}
intOpts := []string{
"memprofilerate",
"blockprofilerate",
}
for _, opt := range intOpts {
if s, found := t.goOpts[opt].(*int); found {
args = append(args, fmt.Sprintf("-%s=%d", opt, *s))
}
}
stringOpts := []string{
"asmflags",
"buildmode",
"compiler",
"gccgoflags",
"installsuffix",
"ldflags",
"pkgdir",
"toolexec",
"coverprofile",
"cpuprofile",
"memprofile",
"outputdir",
"coverpkg",
"tags",
"gcflags",
}
for _, opt := range stringOpts {
if s, found := t.goOpts[opt].(*string); found && *s != "" {
args = append(args, fmt.Sprintf("-%s=%s", opt, *s))
}
}
return args
}
func (t *TestRunner) CompileTo(path string) error {
if t.compiled {
return nil
@ -74,23 +130,7 @@ func (t *TestRunner) CompileTo(path string) error {
return nil
}
args := []string{"test", "-c", "-i", "-o", path, t.Suite.Path}
if t.race {
args = append(args, "-race")
}
if t.cover || t.coverPkg != "" {
args = append(args, "-cover", "-covermode=atomic")
}
if t.coverPkg != "" {
args = append(args, fmt.Sprintf("-coverpkg=%s", t.coverPkg))
}
if t.tags != "" {
args = append(args, fmt.Sprintf("-tags=%s", t.tags))
}
if t.gcFlags != "" {
args = append(args, fmt.Sprintf("-gcflags=%s", t.gcFlags))
}
args := t.BuildArgs(path)
cmd := exec.Command("go", args...)
output, err := cmd.CombinedOutput()
@ -189,6 +229,7 @@ fixCompilationOutput..... rewrites the output to fix the paths.
yeah......
*/
func fixCompilationOutput(output string, relToPath string) string {
relToPath = filepath.Join(relToPath)
re := regexp.MustCompile(`^(\S.*\.go)\:\d+\:`)
lines := strings.Split(output, "\n")
for i, line := range lines {
@ -198,8 +239,10 @@ func fixCompilationOutput(output string, relToPath string) string {
}
path := line[indices[2]:indices[3]]
path = filepath.Join(relToPath, path)
lines[i] = path + line[indices[3]:]
if filepath.Dir(path) != relToPath {
path = filepath.Join(relToPath, path)
lines[i] = path + line[indices[3]:]
}
}
return strings.Join(lines, "\n")
}
@ -281,7 +324,7 @@ func (t *TestRunner) runAndStreamParallelGinkgoSuite() RunResult {
os.Stdout.Sync()
if t.cover || t.coverPkg != "" {
if *t.goOpts["cover"].(*bool) || *t.goOpts["coverpkg"].(*string) != "" || *t.goOpts["covermode"].(*string) != "" {
t.combineCoverprofiles()
}
@ -294,7 +337,7 @@ func (t *TestRunner) runParallelGinkgoSuite() RunResult {
writers := make([]*logWriter, t.numCPU)
reports := make([]*bytes.Buffer, t.numCPU)
stenographer := stenographer.New(!config.DefaultReporterConfig.NoColor)
stenographer := stenographer.New(!config.DefaultReporterConfig.NoColor, config.GinkgoConfig.FlakeAttempts > 1)
aggregator := remote.NewAggregator(t.numCPU, result, config.DefaultReporterConfig, stenographer)
server, err := remote.NewServer(t.numCPU)
@ -363,7 +406,7 @@ func (t *TestRunner) runParallelGinkgoSuite() RunResult {
os.Stdout.Sync()
}
if t.cover || t.coverPkg != "" {
if *t.goOpts["cover"].(*bool) || *t.goOpts["coverpkg"].(*string) != "" || *t.goOpts["covermode"].(*string) != "" {
t.combineCoverprofiles()
}
@ -372,7 +415,7 @@ func (t *TestRunner) runParallelGinkgoSuite() RunResult {
func (t *TestRunner) cmd(ginkgoArgs []string, stream io.Writer, node int) *exec.Cmd {
args := []string{"--test.timeout=24h"}
if t.cover || t.coverPkg != "" {
if *t.goOpts["cover"].(*bool) || *t.goOpts["coverpkg"].(*string) != "" || *t.goOpts["covermode"].(*string) != "" {
coverprofile := "--test.coverprofile=" + t.Suite.PackageName + ".coverprofile"
if t.numCPU > 1 {
coverprofile = fmt.Sprintf("%s.%d", coverprofile, node)

View File

@ -10,6 +10,7 @@ import (
"github.com/onsi/ginkgo/ginkgo/testrunner"
"github.com/onsi/ginkgo/ginkgo/testsuite"
"github.com/onsi/ginkgo/ginkgo/watch"
colorable "github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable"
)
func BuildWatchCommand() *Command {
@ -57,7 +58,7 @@ func (w *SpecWatcher) runnersForSuites(suites []testsuite.TestSuite, additionalA
runners := []*testrunner.TestRunner{}
for _, suite := range suites {
runners = append(runners, testrunner.New(suite, w.commandFlags.NumCPU, w.commandFlags.ParallelStream, w.commandFlags.Race, w.commandFlags.Cover, w.commandFlags.CoverPkg, w.commandFlags.Tags, w.commandFlags.GCFlags, additionalArgs))
runners = append(runners, testrunner.New(suite, w.commandFlags.NumCPU, w.commandFlags.ParallelStream, w.commandFlags.GoOpts, additionalArgs))
}
return runners
@ -96,29 +97,30 @@ func (w *SpecWatcher) WatchSuites(args []string, additionalArgs []string) {
case <-ticker.C:
suites, _ := findSuites(args, w.commandFlags.Recurse, w.commandFlags.SkipPackage, false)
delta, _ := deltaTracker.Delta(suites)
coloredStream := colorable.NewColorableStdout()
suitesToRun := []testsuite.TestSuite{}
if len(delta.NewSuites) > 0 {
fmt.Printf(greenColor+"Detected %d new %s:\n"+defaultStyle, len(delta.NewSuites), pluralizedWord("suite", "suites", len(delta.NewSuites)))
fmt.Fprintf(coloredStream, greenColor+"Detected %d new %s:\n"+defaultStyle, len(delta.NewSuites), pluralizedWord("suite", "suites", len(delta.NewSuites)))
for _, suite := range delta.NewSuites {
suitesToRun = append(suitesToRun, suite.Suite)
fmt.Println(" " + suite.Description())
fmt.Fprintln(coloredStream, " "+suite.Description())
}
}
modifiedSuites := delta.ModifiedSuites()
if len(modifiedSuites) > 0 {
fmt.Println(greenColor + "\nDetected changes in:" + defaultStyle)
fmt.Fprintln(coloredStream, greenColor+"\nDetected changes in:"+defaultStyle)
for _, pkg := range delta.ModifiedPackages {
fmt.Println(" " + pkg)
fmt.Fprintln(coloredStream, " "+pkg)
}
fmt.Printf(greenColor+"Will run %d %s:\n"+defaultStyle, len(modifiedSuites), pluralizedWord("suite", "suites", len(modifiedSuites)))
fmt.Fprintf(coloredStream, greenColor+"Will run %d %s:\n"+defaultStyle, len(modifiedSuites), pluralizedWord("suite", "suites", len(modifiedSuites)))
for _, suite := range modifiedSuites {
suitesToRun = append(suitesToRun, suite.Suite)
fmt.Println(" " + suite.Description())
fmt.Fprintln(coloredStream, " "+suite.Description())
}
fmt.Println("")
fmt.Fprintln(coloredStream, "")
}
if len(suitesToRun) > 0 {
@ -136,7 +138,7 @@ func (w *SpecWatcher) WatchSuites(args []string, additionalArgs []string) {
if result.Passed {
color = greenColor
}
fmt.Println(color + "\nDone. Resuming watch..." + defaultStyle)
fmt.Fprintln(coloredStream, color+"\nDone. Resuming watch..."+defaultStyle)
}
}

View File

@ -68,6 +68,14 @@ type GinkgoTestingT interface {
Fail()
}
//GinkgoRandomSeed returns the seed used to randomize spec execution order. It is
//useful for seeding your own pseudorandom number generators (PRNGs) to ensure
//consistent executions from run to run, where your tests contain variability (for
//example, when selecting random test data).
func GinkgoRandomSeed() int64 {
return config.GinkgoConfig.RandomSeed
}
//GinkgoParallelNode returns the parallel node number for the current ginkgo process
//The node number is 1-indexed
func GinkgoParallelNode() int {
@ -218,7 +226,7 @@ func RunSpecsWithCustomReporters(t GinkgoTestingT, description string, specRepor
func buildDefaultReporter() Reporter {
remoteReportingServer := config.GinkgoConfig.StreamHost
if remoteReportingServer == "" {
stenographer := stenographer.New(!config.DefaultReporterConfig.NoColor)
stenographer := stenographer.New(!config.DefaultReporterConfig.NoColor, config.GinkgoConfig.FlakeAttempts > 1)
return reporters.NewDefaultReporter(config.DefaultReporterConfig, stenographer)
} else {
return remote.NewForwardingReporter(remoteReportingServer, &http.Client{}, remote.NewOutputInterceptor())

View File

@ -239,6 +239,7 @@ func (aggregator *Aggregator) registerSuiteEnding(suite *types.SuiteSummary) (fi
aggregatedSuiteSummary.NumberOfFailedSpecs += suiteSummary.NumberOfFailedSpecs
aggregatedSuiteSummary.NumberOfPendingSpecs += suiteSummary.NumberOfPendingSpecs
aggregatedSuiteSummary.NumberOfSkippedSpecs += suiteSummary.NumberOfSkippedSpecs
aggregatedSuiteSummary.NumberOfFlakedSpecs += suiteSummary.NumberOfFlakedSpecs
}
aggregatedSuiteSummary.RunTime = time.Since(aggregator.startTime)

View File

@ -271,6 +271,9 @@ func (runner *SpecRunner) reportSpecWillRun(summary *types.SpecSummary) {
}
func (runner *SpecRunner) reportSpecDidComplete(summary *types.SpecSummary, failed bool) {
if failed && len(summary.CapturedOutput) == 0 {
summary.CapturedOutput = string(runner.writer.Bytes())
}
for i := len(runner.reporters) - 1; i >= 1; i-- {
runner.reporters[i].SpecDidComplete(summary)
}

View File

@ -50,7 +50,7 @@ func (t *ginkgoTestingTProxy) Log(args ...interface{}) {
}
func (t *ginkgoTestingTProxy) Logf(format string, args ...interface{}) {
fmt.Fprintf(t.writer, format, args...)
t.Log(fmt.Sprintf(format, args...))
}
func (t *ginkgoTestingTProxy) Failed() bool {
@ -65,7 +65,7 @@ func (t *ginkgoTestingTProxy) Skip(args ...interface{}) {
}
func (t *ginkgoTestingTProxy) Skipf(format string, args ...interface{}) {
fmt.Printf(format, args...)
t.Skip(fmt.Sprintf(format, args...))
}
func (t *ginkgoTestingTProxy) SkipNow() {

View File

@ -26,6 +26,11 @@ func (writer *FakeGinkgoWriter) DumpOutWithHeader(header string) {
writer.EventStream = append(writer.EventStream, "DUMP_WITH_HEADER: "+header)
}
func (writer *FakeGinkgoWriter) Bytes() []byte {
writer.EventStream = append(writer.EventStream, "BYTES")
return nil
}
func (writer *FakeGinkgoWriter) Write(data []byte) (n int, err error) {
return 0, nil
}

View File

@ -12,6 +12,7 @@ type WriterInterface interface {
Truncate()
DumpOut()
DumpOutWithHeader(header string)
Bytes() []byte
}
type Writer struct {
@ -40,11 +41,11 @@ func (w *Writer) Write(b []byte) (n int, err error) {
w.lock.Lock()
defer w.lock.Unlock()
n, err = w.buffer.Write(b)
if w.stream {
return w.outWriter.Write(b)
} else {
return w.buffer.Write(b)
}
return n, err
}
func (w *Writer) Truncate() {
@ -61,6 +62,15 @@ func (w *Writer) DumpOut() {
}
}
func (w *Writer) Bytes() []byte {
w.lock.Lock()
defer w.lock.Unlock()
b := w.buffer.Bytes()
copied := make([]byte, len(b))
copy(copied, b)
return copied
}
func (w *Writer) DumpOutWithHeader(header string) {
w.lock.Lock()
defer w.lock.Unlock()

View File

@ -31,6 +31,7 @@ type JUnitTestCase struct {
FailureMessage *JUnitFailureMessage `xml:"failure,omitempty"`
Skipped *JUnitSkipped `xml:"skipped,omitempty"`
Time float64 `xml:"time,attr"`
SystemOut string `xml:"system-out,omitempty"`
}
type JUnitFailureMessage struct {
@ -89,6 +90,7 @@ func (reporter *JUnitReporter) handleSetupSummary(name string, setupSummary *typ
Type: reporter.failureTypeForState(setupSummary.State),
Message: failureMessage(setupSummary.Failure),
}
testCase.SystemOut = setupSummary.CapturedOutput
testCase.Time = setupSummary.RunTime.Seconds()
reporter.suite.TestCases = append(reporter.suite.TestCases, testCase)
}
@ -104,6 +106,7 @@ func (reporter *JUnitReporter) SpecDidComplete(specSummary *types.SpecSummary) {
Type: reporter.failureTypeForState(specSummary.State),
Message: failureMessage(specSummary.Failure),
}
testCase.SystemOut = specSummary.CapturedOutput
}
if specSummary.State == types.SpecStateSkipped || specSummary.State == types.SpecStatePending {
testCase.Skipped = &JUnitSkipped{}

View File

@ -22,24 +22,24 @@ func (s *consoleStenographer) colorize(colorCode string, format string, args ...
}
func (s *consoleStenographer) printBanner(text string, bannerCharacter string) {
fmt.Println(text)
fmt.Println(strings.Repeat(bannerCharacter, len(text)))
fmt.Fprintln(s.w, text)
fmt.Fprintln(s.w, strings.Repeat(bannerCharacter, len(text)))
}
func (s *consoleStenographer) printNewLine() {
fmt.Println("")
fmt.Fprintln(s.w, "")
}
func (s *consoleStenographer) printDelimiter() {
fmt.Println(s.colorize(grayColor, "%s", strings.Repeat("-", 30)))
fmt.Fprintln(s.w, s.colorize(grayColor, "%s", strings.Repeat("-", 30)))
}
func (s *consoleStenographer) print(indentation int, format string, args ...interface{}) {
fmt.Print(s.indent(indentation, format, args...))
fmt.Fprint(s.w, s.indent(indentation, format, args...))
}
func (s *consoleStenographer) println(indentation int, format string, args ...interface{}) {
fmt.Println(s.indent(indentation, format, args...))
fmt.Fprintln(s.w, s.indent(indentation, format, args...))
}
func (s *consoleStenographer) indent(indentation int, format string, args ...interface{}) string {

View File

@ -8,9 +8,11 @@ package stenographer
import (
"fmt"
"io"
"runtime"
"strings"
"github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable"
"github.com/onsi/ginkgo/types"
)
@ -59,22 +61,26 @@ type Stenographer interface {
SummarizeFailures(summaries []*types.SpecSummary)
}
func New(color bool) Stenographer {
func New(color bool, enableFlakes bool) Stenographer {
denoter := "•"
if runtime.GOOS == "windows" {
denoter = "+"
}
return &consoleStenographer{
color: color,
denoter: denoter,
cursorState: cursorStateTop,
color: color,
denoter: denoter,
cursorState: cursorStateTop,
enableFlakes: enableFlakes,
w: colorable.NewColorableStdout(),
}
}
type consoleStenographer struct {
color bool
denoter string
cursorState cursorStateType
color bool
denoter string
cursorState cursorStateType
enableFlakes bool
w io.Writer
}
var alternatingColors = []string{defaultStyle, grayColor}
@ -153,11 +159,16 @@ func (s *consoleStenographer) AnnounceSpecRunCompletion(summary *types.SuiteSumm
status = s.colorize(boldStyle+redColor, "FAIL!")
}
flakes := ""
if s.enableFlakes {
flakes = " | " + s.colorize(yellowColor+boldStyle, "%d Flaked", summary.NumberOfFlakedSpecs)
}
s.print(0,
"%s -- %s | %s | %s | %s ",
status,
s.colorize(greenColor+boldStyle, "%d Passed", summary.NumberOfPassedSpecs),
s.colorize(redColor+boldStyle, "%d Failed", summary.NumberOfFailedSpecs),
s.colorize(redColor+boldStyle, "%d Failed", summary.NumberOfFailedSpecs)+flakes,
s.colorize(yellowColor+boldStyle, "%d Pending", summary.NumberOfPendingSpecs),
s.colorize(cyanColor+boldStyle, "%d Skipped", summary.NumberOfSkippedSpecs),
)

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Yasuhiro Matsumoto
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,43 @@
# go-colorable
Colorable writer for windows.
For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.)
This package is possible to handle escape sequence for ansi color on windows.
## Too Bad!
![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png)
## So Good!
![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png)
## Usage
```go
logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true})
logrus.SetOutput(colorable.NewColorableStdout())
logrus.Info("succeeded")
logrus.Warn("not correct")
logrus.Error("something error")
logrus.Fatal("panic")
```
You can compile above code on non-windows OSs.
## Installation
```
$ go get github.com/mattn/go-colorable
```
# License
MIT
# Author
Yasuhiro Matsumoto (a.k.a mattn)

View File

@ -0,0 +1,24 @@
// +build !windows
package colorable
import (
"io"
"os"
)
func NewColorable(file *os.File) io.Writer {
if file == nil {
panic("nil passed instead of *os.File to NewColorable()")
}
return file
}
func NewColorableStdout() io.Writer {
return os.Stdout
}
func NewColorableStderr() io.Writer {
return os.Stderr
}

View File

@ -0,0 +1,783 @@
package colorable
import (
"bytes"
"fmt"
"io"
"math"
"os"
"strconv"
"strings"
"syscall"
"unsafe"
"github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty"
)
const (
foregroundBlue = 0x1
foregroundGreen = 0x2
foregroundRed = 0x4
foregroundIntensity = 0x8
foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity)
backgroundBlue = 0x10
backgroundGreen = 0x20
backgroundRed = 0x40
backgroundIntensity = 0x80
backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
)
type wchar uint16
type short int16
type dword uint32
type word uint16
type coord struct {
x short
y short
}
type smallRect struct {
left short
top short
right short
bottom short
}
type consoleScreenBufferInfo struct {
size coord
cursorPosition coord
attributes word
window smallRect
maximumWindowSize coord
}
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
)
type Writer struct {
out io.Writer
handle syscall.Handle
lastbuf bytes.Buffer
oldattr word
}
func NewColorable(file *os.File) io.Writer {
if file == nil {
panic("nil passed instead of *os.File to NewColorable()")
}
if isatty.IsTerminal(file.Fd()) {
var csbi consoleScreenBufferInfo
handle := syscall.Handle(file.Fd())
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
return &Writer{out: file, handle: handle, oldattr: csbi.attributes}
} else {
return file
}
}
func NewColorableStdout() io.Writer {
return NewColorable(os.Stdout)
}
func NewColorableStderr() io.Writer {
return NewColorable(os.Stderr)
}
var color256 = map[int]int{
0: 0x000000,
1: 0x800000,
2: 0x008000,
3: 0x808000,
4: 0x000080,
5: 0x800080,
6: 0x008080,
7: 0xc0c0c0,
8: 0x808080,
9: 0xff0000,
10: 0x00ff00,
11: 0xffff00,
12: 0x0000ff,
13: 0xff00ff,
14: 0x00ffff,
15: 0xffffff,
16: 0x000000,
17: 0x00005f,
18: 0x000087,
19: 0x0000af,
20: 0x0000d7,
21: 0x0000ff,
22: 0x005f00,
23: 0x005f5f,
24: 0x005f87,
25: 0x005faf,
26: 0x005fd7,
27: 0x005fff,
28: 0x008700,
29: 0x00875f,
30: 0x008787,
31: 0x0087af,
32: 0x0087d7,
33: 0x0087ff,
34: 0x00af00,
35: 0x00af5f,
36: 0x00af87,
37: 0x00afaf,
38: 0x00afd7,
39: 0x00afff,
40: 0x00d700,
41: 0x00d75f,
42: 0x00d787,
43: 0x00d7af,
44: 0x00d7d7,
45: 0x00d7ff,
46: 0x00ff00,
47: 0x00ff5f,
48: 0x00ff87,
49: 0x00ffaf,
50: 0x00ffd7,
51: 0x00ffff,
52: 0x5f0000,
53: 0x5f005f,
54: 0x5f0087,
55: 0x5f00af,
56: 0x5f00d7,
57: 0x5f00ff,
58: 0x5f5f00,
59: 0x5f5f5f,
60: 0x5f5f87,
61: 0x5f5faf,
62: 0x5f5fd7,
63: 0x5f5fff,
64: 0x5f8700,
65: 0x5f875f,
66: 0x5f8787,
67: 0x5f87af,
68: 0x5f87d7,
69: 0x5f87ff,
70: 0x5faf00,
71: 0x5faf5f,
72: 0x5faf87,
73: 0x5fafaf,
74: 0x5fafd7,
75: 0x5fafff,
76: 0x5fd700,
77: 0x5fd75f,
78: 0x5fd787,
79: 0x5fd7af,
80: 0x5fd7d7,
81: 0x5fd7ff,
82: 0x5fff00,
83: 0x5fff5f,
84: 0x5fff87,
85: 0x5fffaf,
86: 0x5fffd7,
87: 0x5fffff,
88: 0x870000,
89: 0x87005f,
90: 0x870087,
91: 0x8700af,
92: 0x8700d7,
93: 0x8700ff,
94: 0x875f00,
95: 0x875f5f,
96: 0x875f87,
97: 0x875faf,
98: 0x875fd7,
99: 0x875fff,
100: 0x878700,
101: 0x87875f,
102: 0x878787,
103: 0x8787af,
104: 0x8787d7,
105: 0x8787ff,
106: 0x87af00,
107: 0x87af5f,
108: 0x87af87,
109: 0x87afaf,
110: 0x87afd7,
111: 0x87afff,
112: 0x87d700,
113: 0x87d75f,
114: 0x87d787,
115: 0x87d7af,
116: 0x87d7d7,
117: 0x87d7ff,
118: 0x87ff00,
119: 0x87ff5f,
120: 0x87ff87,
121: 0x87ffaf,
122: 0x87ffd7,
123: 0x87ffff,
124: 0xaf0000,
125: 0xaf005f,
126: 0xaf0087,
127: 0xaf00af,
128: 0xaf00d7,
129: 0xaf00ff,
130: 0xaf5f00,
131: 0xaf5f5f,
132: 0xaf5f87,
133: 0xaf5faf,
134: 0xaf5fd7,
135: 0xaf5fff,
136: 0xaf8700,
137: 0xaf875f,
138: 0xaf8787,
139: 0xaf87af,
140: 0xaf87d7,
141: 0xaf87ff,
142: 0xafaf00,
143: 0xafaf5f,
144: 0xafaf87,
145: 0xafafaf,
146: 0xafafd7,
147: 0xafafff,
148: 0xafd700,
149: 0xafd75f,
150: 0xafd787,
151: 0xafd7af,
152: 0xafd7d7,
153: 0xafd7ff,
154: 0xafff00,
155: 0xafff5f,
156: 0xafff87,
157: 0xafffaf,
158: 0xafffd7,
159: 0xafffff,
160: 0xd70000,
161: 0xd7005f,
162: 0xd70087,
163: 0xd700af,
164: 0xd700d7,
165: 0xd700ff,
166: 0xd75f00,
167: 0xd75f5f,
168: 0xd75f87,
169: 0xd75faf,
170: 0xd75fd7,
171: 0xd75fff,
172: 0xd78700,
173: 0xd7875f,
174: 0xd78787,
175: 0xd787af,
176: 0xd787d7,
177: 0xd787ff,
178: 0xd7af00,
179: 0xd7af5f,
180: 0xd7af87,
181: 0xd7afaf,
182: 0xd7afd7,
183: 0xd7afff,
184: 0xd7d700,
185: 0xd7d75f,
186: 0xd7d787,
187: 0xd7d7af,
188: 0xd7d7d7,
189: 0xd7d7ff,
190: 0xd7ff00,
191: 0xd7ff5f,
192: 0xd7ff87,
193: 0xd7ffaf,
194: 0xd7ffd7,
195: 0xd7ffff,
196: 0xff0000,
197: 0xff005f,
198: 0xff0087,
199: 0xff00af,
200: 0xff00d7,
201: 0xff00ff,
202: 0xff5f00,
203: 0xff5f5f,
204: 0xff5f87,
205: 0xff5faf,
206: 0xff5fd7,
207: 0xff5fff,
208: 0xff8700,
209: 0xff875f,
210: 0xff8787,
211: 0xff87af,
212: 0xff87d7,
213: 0xff87ff,
214: 0xffaf00,
215: 0xffaf5f,
216: 0xffaf87,
217: 0xffafaf,
218: 0xffafd7,
219: 0xffafff,
220: 0xffd700,
221: 0xffd75f,
222: 0xffd787,
223: 0xffd7af,
224: 0xffd7d7,
225: 0xffd7ff,
226: 0xffff00,
227: 0xffff5f,
228: 0xffff87,
229: 0xffffaf,
230: 0xffffd7,
231: 0xffffff,
232: 0x080808,
233: 0x121212,
234: 0x1c1c1c,
235: 0x262626,
236: 0x303030,
237: 0x3a3a3a,
238: 0x444444,
239: 0x4e4e4e,
240: 0x585858,
241: 0x626262,
242: 0x6c6c6c,
243: 0x767676,
244: 0x808080,
245: 0x8a8a8a,
246: 0x949494,
247: 0x9e9e9e,
248: 0xa8a8a8,
249: 0xb2b2b2,
250: 0xbcbcbc,
251: 0xc6c6c6,
252: 0xd0d0d0,
253: 0xdadada,
254: 0xe4e4e4,
255: 0xeeeeee,
}
func (w *Writer) Write(data []byte) (n int, err error) {
var csbi consoleScreenBufferInfo
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
er := bytes.NewBuffer(data)
loop:
for {
r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
if r1 == 0 {
break loop
}
c1, _, err := er.ReadRune()
if err != nil {
break loop
}
if c1 != 0x1b {
fmt.Fprint(w.out, string(c1))
continue
}
c2, _, err := er.ReadRune()
if err != nil {
w.lastbuf.WriteRune(c1)
break loop
}
if c2 != 0x5b {
w.lastbuf.WriteRune(c1)
w.lastbuf.WriteRune(c2)
continue
}
var buf bytes.Buffer
var m rune
for {
c, _, err := er.ReadRune()
if err != nil {
w.lastbuf.WriteRune(c1)
w.lastbuf.WriteRune(c2)
w.lastbuf.Write(buf.Bytes())
break loop
}
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
m = c
break
}
buf.Write([]byte(string(c)))
}
var csbi consoleScreenBufferInfo
switch m {
case 'A':
n, err = strconv.Atoi(buf.String())
if err != nil {
continue
}
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.y -= short(n)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'B':
n, err = strconv.Atoi(buf.String())
if err != nil {
continue
}
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.y += short(n)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'C':
n, err = strconv.Atoi(buf.String())
if err != nil {
continue
}
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.x -= short(n)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'D':
n, err = strconv.Atoi(buf.String())
if err != nil {
continue
}
if n, err = strconv.Atoi(buf.String()); err == nil {
var csbi consoleScreenBufferInfo
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.x += short(n)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
}
case 'E':
n, err = strconv.Atoi(buf.String())
if err != nil {
continue
}
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.x = 0
csbi.cursorPosition.y += short(n)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'F':
n, err = strconv.Atoi(buf.String())
if err != nil {
continue
}
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.x = 0
csbi.cursorPosition.y -= short(n)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'G':
n, err = strconv.Atoi(buf.String())
if err != nil {
continue
}
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.x = short(n)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'H':
token := strings.Split(buf.String(), ";")
if len(token) != 2 {
continue
}
n1, err := strconv.Atoi(token[0])
if err != nil {
continue
}
n2, err := strconv.Atoi(token[1])
if err != nil {
continue
}
csbi.cursorPosition.x = short(n2)
csbi.cursorPosition.x = short(n1)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'J':
n, err := strconv.Atoi(buf.String())
if err != nil {
continue
}
var cursor coord
switch n {
case 0:
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
case 1:
cursor = coord{x: csbi.window.left, y: csbi.window.top}
case 2:
cursor = coord{x: csbi.window.left, y: csbi.window.top}
}
var count, written dword
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
case 'K':
n, err := strconv.Atoi(buf.String())
if err != nil {
continue
}
var cursor coord
switch n {
case 0:
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
case 1:
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
case 2:
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
}
var count, written dword
count = dword(csbi.size.x - csbi.cursorPosition.x)
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
case 'm':
attr := csbi.attributes
cs := buf.String()
if cs == "" {
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr))
continue
}
token := strings.Split(cs, ";")
for i := 0; i < len(token); i += 1 {
ns := token[i]
if n, err = strconv.Atoi(ns); err == nil {
switch {
case n == 0 || n == 100:
attr = w.oldattr
case 1 <= n && n <= 5:
attr |= foregroundIntensity
case n == 7:
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
case 22 == n || n == 25 || n == 25:
attr |= foregroundIntensity
case n == 27:
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
case 30 <= n && n <= 37:
attr = (attr & backgroundMask)
if (n-30)&1 != 0 {
attr |= foregroundRed
}
if (n-30)&2 != 0 {
attr |= foregroundGreen
}
if (n-30)&4 != 0 {
attr |= foregroundBlue
}
case n == 38: // set foreground color.
if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") {
if n256, err := strconv.Atoi(token[i+2]); err == nil {
if n256foreAttr == nil {
n256setup()
}
attr &= backgroundMask
attr |= n256foreAttr[n256]
i += 2
}
} else {
attr = attr & (w.oldattr & backgroundMask)
}
case n == 39: // reset foreground color.
attr &= backgroundMask
attr |= w.oldattr & foregroundMask
case 40 <= n && n <= 47:
attr = (attr & foregroundMask)
if (n-40)&1 != 0 {
attr |= backgroundRed
}
if (n-40)&2 != 0 {
attr |= backgroundGreen
}
if (n-40)&4 != 0 {
attr |= backgroundBlue
}
case n == 48: // set background color.
if i < len(token)-2 && token[i+1] == "5" {
if n256, err := strconv.Atoi(token[i+2]); err == nil {
if n256backAttr == nil {
n256setup()
}
attr &= foregroundMask
attr |= n256backAttr[n256]
i += 2
}
} else {
attr = attr & (w.oldattr & foregroundMask)
}
case n == 49: // reset foreground color.
attr &= foregroundMask
attr |= w.oldattr & backgroundMask
case 90 <= n && n <= 97:
attr = (attr & backgroundMask)
attr |= foregroundIntensity
if (n-90)&1 != 0 {
attr |= foregroundRed
}
if (n-90)&2 != 0 {
attr |= foregroundGreen
}
if (n-90)&4 != 0 {
attr |= foregroundBlue
}
case 100 <= n && n <= 107:
attr = (attr & foregroundMask)
attr |= backgroundIntensity
if (n-100)&1 != 0 {
attr |= backgroundRed
}
if (n-100)&2 != 0 {
attr |= backgroundGreen
}
if (n-100)&4 != 0 {
attr |= backgroundBlue
}
}
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr))
}
}
}
}
return len(data) - w.lastbuf.Len(), nil
}
type consoleColor struct {
rgb int
red bool
green bool
blue bool
intensity bool
}
func (c consoleColor) foregroundAttr() (attr word) {
if c.red {
attr |= foregroundRed
}
if c.green {
attr |= foregroundGreen
}
if c.blue {
attr |= foregroundBlue
}
if c.intensity {
attr |= foregroundIntensity
}
return
}
func (c consoleColor) backgroundAttr() (attr word) {
if c.red {
attr |= backgroundRed
}
if c.green {
attr |= backgroundGreen
}
if c.blue {
attr |= backgroundBlue
}
if c.intensity {
attr |= backgroundIntensity
}
return
}
var color16 = []consoleColor{
consoleColor{0x000000, false, false, false, false},
consoleColor{0x000080, false, false, true, false},
consoleColor{0x008000, false, true, false, false},
consoleColor{0x008080, false, true, true, false},
consoleColor{0x800000, true, false, false, false},
consoleColor{0x800080, true, false, true, false},
consoleColor{0x808000, true, true, false, false},
consoleColor{0xc0c0c0, true, true, true, false},
consoleColor{0x808080, false, false, false, true},
consoleColor{0x0000ff, false, false, true, true},
consoleColor{0x00ff00, false, true, false, true},
consoleColor{0x00ffff, false, true, true, true},
consoleColor{0xff0000, true, false, false, true},
consoleColor{0xff00ff, true, false, true, true},
consoleColor{0xffff00, true, true, false, true},
consoleColor{0xffffff, true, true, true, true},
}
type hsv struct {
h, s, v float32
}
func (a hsv) dist(b hsv) float32 {
dh := a.h - b.h
switch {
case dh > 0.5:
dh = 1 - dh
case dh < -0.5:
dh = -1 - dh
}
ds := a.s - b.s
dv := a.v - b.v
return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv)))
}
func toHSV(rgb int) hsv {
r, g, b := float32((rgb&0xFF0000)>>16)/256.0,
float32((rgb&0x00FF00)>>8)/256.0,
float32(rgb&0x0000FF)/256.0
min, max := minmax3f(r, g, b)
h := max - min
if h > 0 {
if max == r {
h = (g - b) / h
if h < 0 {
h += 6
}
} else if max == g {
h = 2 + (b-r)/h
} else {
h = 4 + (r-g)/h
}
}
h /= 6.0
s := max - min
if max != 0 {
s /= max
}
v := max
return hsv{h: h, s: s, v: v}
}
type hsvTable []hsv
func toHSVTable(rgbTable []consoleColor) hsvTable {
t := make(hsvTable, len(rgbTable))
for i, c := range rgbTable {
t[i] = toHSV(c.rgb)
}
return t
}
func (t hsvTable) find(rgb int) consoleColor {
hsv := toHSV(rgb)
n := 7
l := float32(5.0)
for i, p := range t {
d := hsv.dist(p)
if d < l {
l, n = d, i
}
}
return color16[n]
}
func minmax3f(a, b, c float32) (min, max float32) {
if a < b {
if b < c {
return a, c
} else if a < c {
return a, b
} else {
return c, b
}
} else {
if a < c {
return b, c
} else if b < c {
return b, a
} else {
return c, a
}
}
}
var n256foreAttr []word
var n256backAttr []word
func n256setup() {
n256foreAttr = make([]word, 256)
n256backAttr = make([]word, 256)
t := toHSVTable(color16)
for i, rgb := range color256 {
c := t.find(rgb)
n256foreAttr[i] = c.foregroundAttr()
n256backAttr[i] = c.backgroundAttr()
}
}

View File

@ -0,0 +1,57 @@
package colorable
import (
"bytes"
"fmt"
"io"
)
type NonColorable struct {
out io.Writer
lastbuf bytes.Buffer
}
func NewNonColorable(w io.Writer) io.Writer {
return &NonColorable{out: w}
}
func (w *NonColorable) Write(data []byte) (n int, err error) {
er := bytes.NewBuffer(data)
loop:
for {
c1, _, err := er.ReadRune()
if err != nil {
break loop
}
if c1 != 0x1b {
fmt.Fprint(w.out, string(c1))
continue
}
c2, _, err := er.ReadRune()
if err != nil {
w.lastbuf.WriteRune(c1)
break loop
}
if c2 != 0x5b {
w.lastbuf.WriteRune(c1)
w.lastbuf.WriteRune(c2)
continue
}
var buf bytes.Buffer
for {
c, _, err := er.ReadRune()
if err != nil {
w.lastbuf.WriteRune(c1)
w.lastbuf.WriteRune(c2)
w.lastbuf.Write(buf.Bytes())
break loop
}
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
break
}
buf.Write([]byte(string(c)))
}
}
return len(data) - w.lastbuf.Len(), nil
}

View File

@ -0,0 +1,9 @@
Copyright (c) Yasuhiro MATSUMOTO <mattn.jp@gmail.com>
MIT License (Expat)
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,37 @@
# go-isatty
isatty for golang
## Usage
```go
package main
import (
"fmt"
"github.com/mattn/go-isatty"
"os"
)
func main() {
if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Println("Is Terminal")
} else {
fmt.Println("Is Not Terminal")
}
}
```
## Installation
```
$ go get github.com/mattn/go-isatty
```
# License
MIT
# Author
Yasuhiro Matsumoto (a.k.a mattn)

View File

@ -0,0 +1,2 @@
// Package isatty implements interface to isatty
package isatty

View File

@ -0,0 +1,9 @@
// +build appengine
package isatty
// IsTerminal returns true if the file descriptor is terminal which
// is always false on on appengine classic which is a sandboxed PaaS.
func IsTerminal(fd uintptr) bool {
return false
}

View File

@ -0,0 +1,18 @@
// +build darwin freebsd openbsd netbsd
// +build !appengine
package isatty
import (
"syscall"
"unsafe"
)
const ioctlReadTermios = syscall.TIOCGETA
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var termios syscall.Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}

View File

@ -0,0 +1,18 @@
// +build linux
// +build !appengine
package isatty
import (
"syscall"
"unsafe"
)
const ioctlReadTermios = syscall.TCGETS
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var termios syscall.Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}

View File

@ -0,0 +1,16 @@
// +build solaris
// +build !appengine
package isatty
import (
"golang.org/x/sys/unix"
)
// IsTerminal returns true if the given file descriptor is a terminal.
// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c
func IsTerminal(fd uintptr) bool {
var termio unix.Termio
err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio)
return err == nil
}

View File

@ -0,0 +1,19 @@
// +build windows
// +build !appengine
package isatty
import (
"syscall"
"unsafe"
)
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var st uint32
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
return r != 0 && e == 0
}