Upstream gookit/color state of the touched files before a10b061e0b
This commit is contained in:
commit
baec7b1239
2 files changed
+1397
No files matched your search
@@ -0,0 +1,733 @@
|
||||
package color
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
var (
|
||||
// NoColor defines if the output is colorized or not. It's dynamically set to
|
||||
// false or true based on the stdout's file descriptor referring to a terminal
|
||||
// or not. It's also set to true if the NO_COLOR environment variable is
|
||||
// set (regardless of its value). This is a global option and affects all
|
||||
// colors. For more control over each color block use the methods
|
||||
// DisableColor() individually.
|
||||
NoColor = noColorIsSet() || os.Getenv("TERM") == "dumb" || !stdoutIsTerminal()
|
||||
|
||||
// Output defines the standard output of the print functions. By default,
|
||||
// stdOut() is used.
|
||||
Output = stdOut()
|
||||
|
||||
// Error defines the standard error of the print functions. By default,
|
||||
// stdErr() is used.
|
||||
Error = stdErr()
|
||||
|
||||
// colorsCache is used to reduce the count of created Color objects and
|
||||
// allows to reuse already created objects with required Attribute.
|
||||
colorsCache = make(map[Attribute]*Color)
|
||||
colorsCacheMu sync.Mutex // protects colorsCache
|
||||
)
|
||||
|
||||
// noColorIsSet returns true if the environment variable NO_COLOR is set to a non-empty string.
|
||||
func noColorIsSet() bool {
|
||||
return os.Getenv("NO_COLOR") != ""
|
||||
}
|
||||
|
||||
// stdoutIsTerminal returns true if os.Stdout is a terminal.
|
||||
// Returns false if os.Stdout is nil (e.g., when running as a Windows service).
|
||||
func stdoutIsTerminal() bool {
|
||||
if os.Stdout == nil {
|
||||
return false
|
||||
}
|
||||
return isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())
|
||||
}
|
||||
|
||||
// stdOut returns a writer for color output.
|
||||
// Returns io.Discard if os.Stdout is nil (e.g., when running as a Windows service).
|
||||
func stdOut() io.Writer {
|
||||
if os.Stdout == nil {
|
||||
return io.Discard
|
||||
}
|
||||
return colorable.NewColorableStdout()
|
||||
}
|
||||
|
||||
// stdErr returns a writer for color error output.
|
||||
// Returns io.Discard if os.Stderr is nil (e.g., when running as a Windows service).
|
||||
func stdErr() io.Writer {
|
||||
if os.Stderr == nil {
|
||||
return io.Discard
|
||||
}
|
||||
return colorable.NewColorableStderr()
|
||||
}
|
||||
|
||||
// Color defines a custom color object which is defined by SGR parameters.
|
||||
type Color struct {
|
||||
params []Attribute
|
||||
noColor *bool
|
||||
}
|
||||
|
||||
// Attribute defines a single SGR Code
|
||||
type Attribute int
|
||||
|
||||
const escape = "\x1b"
|
||||
|
||||
// Base attributes
|
||||
const (
|
||||
Reset Attribute = iota
|
||||
Bold
|
||||
Faint
|
||||
Italic
|
||||
Underline
|
||||
BlinkSlow
|
||||
BlinkRapid
|
||||
ReverseVideo
|
||||
Concealed
|
||||
CrossedOut
|
||||
)
|
||||
|
||||
const (
|
||||
ResetBold Attribute = iota + 22
|
||||
ResetItalic
|
||||
ResetUnderline
|
||||
ResetBlinking
|
||||
_
|
||||
ResetReversed
|
||||
ResetConcealed
|
||||
ResetCrossedOut
|
||||
)
|
||||
|
||||
var mapResetAttributes map[Attribute]Attribute = map[Attribute]Attribute{
|
||||
Bold: ResetBold,
|
||||
Faint: ResetBold,
|
||||
Italic: ResetItalic,
|
||||
Underline: ResetUnderline,
|
||||
BlinkSlow: ResetBlinking,
|
||||
BlinkRapid: ResetBlinking,
|
||||
ReverseVideo: ResetReversed,
|
||||
Concealed: ResetConcealed,
|
||||
CrossedOut: ResetCrossedOut,
|
||||
}
|
||||
|
||||
// Foreground text colors
|
||||
const (
|
||||
FgBlack Attribute = iota + 30
|
||||
FgRed
|
||||
FgGreen
|
||||
FgYellow
|
||||
FgBlue
|
||||
FgMagenta
|
||||
FgCyan
|
||||
FgWhite
|
||||
|
||||
// used internally for 256 and 24-bit coloring
|
||||
foreground
|
||||
)
|
||||
|
||||
// Foreground Hi-Intensity text colors
|
||||
const (
|
||||
FgHiBlack Attribute = iota + 90
|
||||
FgHiRed
|
||||
FgHiGreen
|
||||
FgHiYellow
|
||||
FgHiBlue
|
||||
FgHiMagenta
|
||||
FgHiCyan
|
||||
FgHiWhite
|
||||
)
|
||||
|
||||
// Background text colors
|
||||
const (
|
||||
BgBlack Attribute = iota + 40
|
||||
BgRed
|
||||
BgGreen
|
||||
BgYellow
|
||||
BgBlue
|
||||
BgMagenta
|
||||
BgCyan
|
||||
BgWhite
|
||||
|
||||
// used internally for 256 and 24-bit coloring
|
||||
background
|
||||
)
|
||||
|
||||
// Background Hi-Intensity text colors
|
||||
const (
|
||||
BgHiBlack Attribute = iota + 100
|
||||
BgHiRed
|
||||
BgHiGreen
|
||||
BgHiYellow
|
||||
BgHiBlue
|
||||
BgHiMagenta
|
||||
BgHiCyan
|
||||
BgHiWhite
|
||||
)
|
||||
|
||||
// New returns a newly created color object.
|
||||
func New(value ...Attribute) *Color {
|
||||
c := &Color{
|
||||
params: make([]Attribute, 0),
|
||||
}
|
||||
|
||||
if noColorIsSet() {
|
||||
c.noColor = boolPtr(true)
|
||||
}
|
||||
|
||||
c.Add(value...)
|
||||
return c
|
||||
}
|
||||
|
||||
// RGB returns a new foreground color in 24-bit RGB.
|
||||
func RGB(r, g, b int) *Color {
|
||||
return New(foreground, 2, Attribute(r), Attribute(g), Attribute(b))
|
||||
}
|
||||
|
||||
// BgRGB returns a new background color in 24-bit RGB.
|
||||
func BgRGB(r, g, b int) *Color {
|
||||
return New(background, 2, Attribute(r), Attribute(g), Attribute(b))
|
||||
}
|
||||
|
||||
// AddRGB is used to chain foreground RGB SGR parameters. Use as many as parameters to combine
|
||||
// and create custom color objects. Example: .Add(34, 0, 12).Add(255, 128, 0).
|
||||
func (c *Color) AddRGB(r, g, b int) *Color {
|
||||
c.params = append(c.params, foreground, 2, Attribute(r), Attribute(g), Attribute(b))
|
||||
return c
|
||||
}
|
||||
|
||||
// AddRGB is used to chain background RGB SGR parameters. Use as many as parameters to combine
|
||||
// and create custom color objects. Example: .Add(34, 0, 12).Add(255, 128, 0).
|
||||
func (c *Color) AddBgRGB(r, g, b int) *Color {
|
||||
c.params = append(c.params, background, 2, Attribute(r), Attribute(g), Attribute(b))
|
||||
return c
|
||||
}
|
||||
|
||||
// Set sets the given parameters immediately. It will change the color of
|
||||
// output with the given SGR parameters until color.Unset() is called.
|
||||
func Set(p ...Attribute) *Color {
|
||||
c := New(p...)
|
||||
c.Set()
|
||||
return c
|
||||
}
|
||||
|
||||
// Unset resets all escape attributes and clears the output. Usually should
|
||||
// be called after Set().
|
||||
func Unset() {
|
||||
if NoColor {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(Output, "%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// Set sets the SGR sequence.
|
||||
func (c *Color) Set() *Color {
|
||||
if c.isNoColorSet() {
|
||||
return c
|
||||
}
|
||||
|
||||
fmt.Fprint(Output, c.format())
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Color) unset() {
|
||||
if c.isNoColorSet() {
|
||||
return
|
||||
}
|
||||
|
||||
Unset()
|
||||
}
|
||||
|
||||
// SetWriter is used to set the SGR sequence with the given io.Writer. This is
|
||||
// a low-level function, and users should use the higher-level functions, such
|
||||
// as color.Fprint, color.Print, etc.
|
||||
func (c *Color) SetWriter(w io.Writer) *Color {
|
||||
_, _ = c.setWriter(w)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Color) setWriter(w io.Writer) (int, error) {
|
||||
if c.isNoColorSet() {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return fmt.Fprint(w, c.format())
|
||||
}
|
||||
|
||||
// UnsetWriter resets all escape attributes and clears the output with the give
|
||||
// io.Writer. Usually should be called after SetWriter().
|
||||
func (c *Color) UnsetWriter(w io.Writer) {
|
||||
_, _ = c.unsetWriter(w)
|
||||
}
|
||||
|
||||
func (c *Color) unsetWriter(w io.Writer) (int, error) {
|
||||
if c.isNoColorSet() {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return fmt.Fprintf(w, "%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// Add is used to chain SGR parameters. Use as many as parameters to combine
|
||||
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
|
||||
func (c *Color) Add(value ...Attribute) *Color {
|
||||
c.params = append(c.params, value...)
|
||||
return c
|
||||
}
|
||||
|
||||
// Fprint formats using the default formats for its operands and writes to w.
|
||||
// Spaces are added between operands when neither is a string.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
n, err = c.setWriter(w)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
nn, err := fmt.Fprint(w, a...)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
nn, err = c.unsetWriter(w)
|
||||
n += nn
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Print formats using the default formats for its operands and writes to
|
||||
// standard output. Spaces are added between operands when neither is a
|
||||
// string. It returns the number of bytes written and any write error
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Print(a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprint(Output, a...)
|
||||
}
|
||||
|
||||
// Fprintf formats according to a format specifier and writes to w.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||
n, err = c.setWriter(w)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
nn, err := fmt.Fprintf(w, format, a...)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
nn, err = c.unsetWriter(w)
|
||||
n += nn
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Printf formats according to a format specifier and writes to standard output.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// This is the standard fmt.Printf() method wrapped with the given color.
|
||||
func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprintf(Output, format, a...)
|
||||
}
|
||||
|
||||
// Fprintln formats using the default formats for its operands and writes to w.
|
||||
// Spaces are always added between operands and a newline is appended.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintln(w, c.wrap(sprintln(a...)))
|
||||
}
|
||||
|
||||
// Println formats using the default formats for its operands and writes to
|
||||
// standard output. Spaces are always added between operands and a newline is
|
||||
// appended. It returns the number of bytes written and any write error
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Println(a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintln(Output, c.wrap(sprintln(a...)))
|
||||
}
|
||||
|
||||
// Sprint is just like Print, but returns a string instead of printing it.
|
||||
func (c *Color) Sprint(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprint(a...))
|
||||
}
|
||||
|
||||
// Sprintln is just like Println, but returns a string instead of printing it.
|
||||
func (c *Color) Sprintln(a ...interface{}) string {
|
||||
return c.wrap(sprintln(a...)) + "\n"
|
||||
}
|
||||
|
||||
// Sprintf is just like Printf, but returns a string instead of printing it.
|
||||
func (c *Color) Sprintf(format string, a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// FprintFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprint().
|
||||
func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) {
|
||||
return func(w io.Writer, a ...interface{}) {
|
||||
c.Fprint(w, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Print().
|
||||
func (c *Color) PrintFunc() func(a ...interface{}) {
|
||||
return func(a ...interface{}) {
|
||||
c.Print(a...)
|
||||
}
|
||||
}
|
||||
|
||||
// FprintfFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprintf().
|
||||
func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) {
|
||||
return func(w io.Writer, format string, a ...interface{}) {
|
||||
c.Fprintf(w, format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintfFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Printf().
|
||||
func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
|
||||
return func(format string, a ...interface{}) {
|
||||
c.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// FprintlnFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprintln().
|
||||
func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) {
|
||||
return func(w io.Writer, a ...interface{}) {
|
||||
c.Fprintln(w, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintlnFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Println().
|
||||
func (c *Color) PrintlnFunc() func(a ...interface{}) {
|
||||
return func(a ...interface{}) {
|
||||
c.Println(a...)
|
||||
}
|
||||
}
|
||||
|
||||
// SprintFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprint(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output, example:
|
||||
//
|
||||
// put := New(FgYellow).SprintFunc()
|
||||
// fmt.Fprintf(color.Output, "This is a %s", put("warning"))
|
||||
func (c *Color) SprintFunc() func(a ...interface{}) string {
|
||||
return func(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprint(a...))
|
||||
}
|
||||
}
|
||||
|
||||
// SprintfFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output.
|
||||
func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
|
||||
return func(format string, a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintf(format, a...))
|
||||
}
|
||||
}
|
||||
|
||||
// SprintlnFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output.
|
||||
func (c *Color) SprintlnFunc() func(a ...interface{}) string {
|
||||
return func(a ...interface{}) string {
|
||||
return c.wrap(sprintln(a...)) + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m"
|
||||
// an example output might be: "1;36" -> bold cyan
|
||||
func (c *Color) sequence() string {
|
||||
format := make([]string, len(c.params))
|
||||
for i, v := range c.params {
|
||||
format[i] = strconv.Itoa(int(v))
|
||||
}
|
||||
|
||||
return strings.Join(format, ";")
|
||||
}
|
||||
|
||||
// wrap wraps the s string with the colors attributes. The string is ready to
|
||||
// be printed.
|
||||
func (c *Color) wrap(s string) string {
|
||||
if c.isNoColorSet() {
|
||||
return s
|
||||
}
|
||||
|
||||
return c.format() + s + c.unformat()
|
||||
}
|
||||
|
||||
func (c *Color) format() string {
|
||||
return fmt.Sprintf("%s[%sm", escape, c.sequence())
|
||||
}
|
||||
|
||||
func (c *Color) unformat() string {
|
||||
//return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||
//for each element in sequence let's use the specific reset escape, or the generic one if not found
|
||||
format := make([]string, len(c.params))
|
||||
for i, v := range c.params {
|
||||
format[i] = strconv.Itoa(int(Reset))
|
||||
ra, ok := mapResetAttributes[v]
|
||||
if ok {
|
||||
format[i] = strconv.Itoa(int(ra))
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s[%sm", escape, strings.Join(format, ";"))
|
||||
}
|
||||
|
||||
// DisableColor disables the color output. Useful to not change any existing
|
||||
// code and still being able to output. Can be used for flags like
|
||||
// "--no-color". To enable back use EnableColor() method.
|
||||
func (c *Color) DisableColor() {
|
||||
c.noColor = boolPtr(true)
|
||||
}
|
||||
|
||||
// EnableColor enables the color output. Use it in conjunction with
|
||||
// DisableColor(). Otherwise, this method has no side effects.
|
||||
func (c *Color) EnableColor() {
|
||||
c.noColor = boolPtr(false)
|
||||
}
|
||||
|
||||
func (c *Color) isNoColorSet() bool {
|
||||
// check first if we have user set action
|
||||
if c.noColor != nil {
|
||||
return *c.noColor
|
||||
}
|
||||
|
||||
// if not return the global option, which is disabled by default
|
||||
return NoColor
|
||||
}
|
||||
|
||||
// Equals returns a boolean value indicating whether two colors are equal.
|
||||
func (c *Color) Equals(c2 *Color) bool {
|
||||
if c == nil && c2 == nil {
|
||||
return true
|
||||
}
|
||||
if c == nil || c2 == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(c.params) != len(c2.params) {
|
||||
return false
|
||||
}
|
||||
|
||||
counts := make(map[Attribute]int, len(c.params))
|
||||
for _, attr := range c.params {
|
||||
counts[attr]++
|
||||
}
|
||||
|
||||
for _, attr := range c2.params {
|
||||
if counts[attr] == 0 {
|
||||
return false
|
||||
}
|
||||
counts[attr]--
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func boolPtr(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
func getCachedColor(p Attribute) *Color {
|
||||
colorsCacheMu.Lock()
|
||||
defer colorsCacheMu.Unlock()
|
||||
|
||||
c, ok := colorsCache[p]
|
||||
if !ok {
|
||||
c = New(p)
|
||||
colorsCache[p] = c
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func colorPrint(format string, p Attribute, a ...interface{}) {
|
||||
c := getCachedColor(p)
|
||||
|
||||
if !strings.HasSuffix(format, "\n") {
|
||||
format += "\n"
|
||||
}
|
||||
|
||||
if len(a) == 0 {
|
||||
c.Print(format)
|
||||
} else {
|
||||
c.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
func colorString(format string, p Attribute, a ...interface{}) string {
|
||||
c := getCachedColor(p)
|
||||
|
||||
if len(a) == 0 {
|
||||
return c.SprintFunc()(format)
|
||||
}
|
||||
|
||||
return c.SprintfFunc()(format, a...)
|
||||
}
|
||||
|
||||
// Black is a convenient helper function to print with black foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
|
||||
|
||||
// Red is a convenient helper function to print with red foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
|
||||
|
||||
// Green is a convenient helper function to print with green foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
|
||||
|
||||
// Yellow is a convenient helper function to print with yellow foreground.
|
||||
// A newline is appended to format by default.
|
||||
func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
|
||||
|
||||
// Blue is a convenient helper function to print with blue foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
|
||||
|
||||
// Magenta is a convenient helper function to print with magenta foreground.
|
||||
// A newline is appended to format by default.
|
||||
func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
|
||||
|
||||
// Cyan is a convenient helper function to print with cyan foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
|
||||
|
||||
// White is a convenient helper function to print with white foreground. A
|
||||
// newline is appended to format by default.
|
||||
func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
|
||||
|
||||
// BlackString is a convenient helper function to return a string with black
|
||||
// foreground.
|
||||
func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
|
||||
|
||||
// RedString is a convenient helper function to return a string with red
|
||||
// foreground.
|
||||
func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
|
||||
|
||||
// GreenString is a convenient helper function to return a string with green
|
||||
// foreground.
|
||||
func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
|
||||
|
||||
// YellowString is a convenient helper function to return a string with yellow
|
||||
// foreground.
|
||||
func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
|
||||
|
||||
// BlueString is a convenient helper function to return a string with blue
|
||||
// foreground.
|
||||
func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
|
||||
|
||||
// MagentaString is a convenient helper function to return a string with magenta
|
||||
// foreground.
|
||||
func MagentaString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgMagenta, a...)
|
||||
}
|
||||
|
||||
// CyanString is a convenient helper function to return a string with cyan
|
||||
// foreground.
|
||||
func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
|
||||
|
||||
// WhiteString is a convenient helper function to return a string with white
|
||||
// foreground.
|
||||
func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
|
||||
|
||||
// HiBlack is a convenient helper function to print with hi-intensity black foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
|
||||
|
||||
// HiRed is a convenient helper function to print with hi-intensity red foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
|
||||
|
||||
// HiGreen is a convenient helper function to print with hi-intensity green foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
|
||||
|
||||
// HiYellow is a convenient helper function to print with hi-intensity yellow foreground.
|
||||
// A newline is appended to format by default.
|
||||
func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
|
||||
|
||||
// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
|
||||
|
||||
// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground.
|
||||
// A newline is appended to format by default.
|
||||
func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
|
||||
|
||||
// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
|
||||
|
||||
// HiWhite is a convenient helper function to print with hi-intensity white foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
|
||||
|
||||
// HiBlackString is a convenient helper function to return a string with hi-intensity black
|
||||
// foreground.
|
||||
func HiBlackString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiBlack, a...)
|
||||
}
|
||||
|
||||
// HiRedString is a convenient helper function to return a string with hi-intensity red
|
||||
// foreground.
|
||||
func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
|
||||
|
||||
// HiGreenString is a convenient helper function to return a string with hi-intensity green
|
||||
// foreground.
|
||||
func HiGreenString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiGreen, a...)
|
||||
}
|
||||
|
||||
// HiYellowString is a convenient helper function to return a string with hi-intensity yellow
|
||||
// foreground.
|
||||
func HiYellowString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiYellow, a...)
|
||||
}
|
||||
|
||||
// HiBlueString is a convenient helper function to return a string with hi-intensity blue
|
||||
// foreground.
|
||||
func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
|
||||
|
||||
// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta
|
||||
// foreground.
|
||||
func HiMagentaString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiMagenta, a...)
|
||||
}
|
||||
|
||||
// HiCyanString is a convenient helper function to return a string with hi-intensity cyan
|
||||
// foreground.
|
||||
func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
|
||||
|
||||
// HiWhiteString is a convenient helper function to return a string with hi-intensity white
|
||||
// foreground.
|
||||
func HiWhiteString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiWhite, a...)
|
||||
}
|
||||
|
||||
// sprintln is a helper function to format a string with fmt.Sprintln and trim the trailing newline.
|
||||
func sprintln(a ...interface{}) string {
|
||||
return strings.TrimSuffix(fmt.Sprintln(a...), "\n")
|
||||
}
|
||||
+664
@@ -0,0 +1,664 @@
|
||||
package color
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattn/go-colorable"
|
||||
)
|
||||
|
||||
// Testing colors is kinda different. First we test for given colors and their
|
||||
// escaped formatted results. Next we create some visual tests to be tested.
|
||||
// Each visual test includes the color name to be compared.
|
||||
func TestColor(t *testing.T) {
|
||||
rb := new(bytes.Buffer)
|
||||
Output = rb
|
||||
|
||||
NoColor = false
|
||||
|
||||
testColors := []struct {
|
||||
text string
|
||||
code Attribute
|
||||
}{
|
||||
{text: "black", code: FgBlack},
|
||||
{text: "red", code: FgRed},
|
||||
{text: "green", code: FgGreen},
|
||||
{text: "yellow", code: FgYellow},
|
||||
{text: "blue", code: FgBlue},
|
||||
{text: "magent", code: FgMagenta},
|
||||
{text: "cyan", code: FgCyan},
|
||||
{text: "white", code: FgWhite},
|
||||
{text: "hblack", code: FgHiBlack},
|
||||
{text: "hred", code: FgHiRed},
|
||||
{text: "hgreen", code: FgHiGreen},
|
||||
{text: "hyellow", code: FgHiYellow},
|
||||
{text: "hblue", code: FgHiBlue},
|
||||
{text: "hmagent", code: FgHiMagenta},
|
||||
{text: "hcyan", code: FgHiCyan},
|
||||
{text: "hwhite", code: FgHiWhite},
|
||||
}
|
||||
|
||||
for _, c := range testColors {
|
||||
New(c.code).Print(c.text)
|
||||
|
||||
line, _ := rb.ReadString('\n')
|
||||
scannedLine := fmt.Sprintf("%q", line)
|
||||
colored := fmt.Sprintf("\x1b[%dm%s\x1b[0m", c.code, c.text)
|
||||
escapedForm := fmt.Sprintf("%q", colored)
|
||||
|
||||
fmt.Printf("%s\t: %s\n", c.text, line)
|
||||
|
||||
if scannedLine != escapedForm {
|
||||
t.Errorf("Expecting %s, got '%s'\n", escapedForm, scannedLine)
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range testColors {
|
||||
line := New(c.code).Sprintf("%s", c.text)
|
||||
scannedLine := fmt.Sprintf("%q", line)
|
||||
colored := fmt.Sprintf("\x1b[%dm%s\x1b[0m", c.code, c.text)
|
||||
escapedForm := fmt.Sprintf("%q", colored)
|
||||
|
||||
fmt.Printf("%s\t: %s\n", c.text, line)
|
||||
|
||||
if scannedLine != escapedForm {
|
||||
t.Errorf("Expecting %s, got '%s'\n", escapedForm, scannedLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestColorEquals(t *testing.T) {
|
||||
fgblack1 := New(FgBlack)
|
||||
fgblack2 := New(FgBlack)
|
||||
bgblack := New(BgBlack)
|
||||
fgbgblack := New(FgBlack, BgBlack)
|
||||
fgblackbgred := New(FgBlack, BgRed)
|
||||
fgred := New(FgRed)
|
||||
bgred := New(BgRed)
|
||||
|
||||
if !fgblack1.Equals(fgblack2) {
|
||||
t.Error("Two black colors are not equal")
|
||||
}
|
||||
|
||||
if fgblack1.Equals(bgblack) {
|
||||
t.Error("Fg and bg black colors are equal")
|
||||
}
|
||||
|
||||
if fgblack1.Equals(fgbgblack) {
|
||||
t.Error("Fg black equals fg/bg black color")
|
||||
}
|
||||
|
||||
if fgblack1.Equals(fgred) {
|
||||
t.Error("Fg black equals Fg red")
|
||||
}
|
||||
|
||||
if fgblack1.Equals(bgred) {
|
||||
t.Error("Fg black equals Bg red")
|
||||
}
|
||||
|
||||
if fgblack1.Equals(fgblackbgred) {
|
||||
t.Error("Fg black equals fg black bg red")
|
||||
}
|
||||
}
|
||||
|
||||
func TestColorEquals_DuplicateAttributes(t *testing.T) {
|
||||
ordered := New(FgRed, Bold).Add(FgRed)
|
||||
reordered := New(Bold, FgRed).Add(FgRed)
|
||||
differentCounts := New(FgRed, Bold).Add(Bold)
|
||||
|
||||
if !ordered.Equals(reordered) {
|
||||
t.Error("Colors with the same attributes in different orders are not equal")
|
||||
}
|
||||
|
||||
if ordered.Equals(differentCounts) {
|
||||
t.Error("Colors with different duplicate attribute counts are equal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoColor(t *testing.T) {
|
||||
rb := new(bytes.Buffer)
|
||||
Output = rb
|
||||
|
||||
testColors := []struct {
|
||||
text string
|
||||
code Attribute
|
||||
}{
|
||||
{text: "black", code: FgBlack},
|
||||
{text: "red", code: FgRed},
|
||||
{text: "green", code: FgGreen},
|
||||
{text: "yellow", code: FgYellow},
|
||||
{text: "blue", code: FgBlue},
|
||||
{text: "magent", code: FgMagenta},
|
||||
{text: "cyan", code: FgCyan},
|
||||
{text: "white", code: FgWhite},
|
||||
{text: "hblack", code: FgHiBlack},
|
||||
{text: "hred", code: FgHiRed},
|
||||
{text: "hgreen", code: FgHiGreen},
|
||||
{text: "hyellow", code: FgHiYellow},
|
||||
{text: "hblue", code: FgHiBlue},
|
||||
{text: "hmagent", code: FgHiMagenta},
|
||||
{text: "hcyan", code: FgHiCyan},
|
||||
{text: "hwhite", code: FgHiWhite},
|
||||
}
|
||||
|
||||
for _, c := range testColors {
|
||||
p := New(c.code)
|
||||
p.DisableColor()
|
||||
p.Print(c.text)
|
||||
|
||||
line, _ := rb.ReadString('\n')
|
||||
if line != c.text {
|
||||
t.Errorf("Expecting %s, got '%s'\n", c.text, line)
|
||||
}
|
||||
}
|
||||
|
||||
// global check
|
||||
NoColor = true
|
||||
t.Cleanup(func() {
|
||||
NoColor = false
|
||||
})
|
||||
|
||||
for _, c := range testColors {
|
||||
p := New(c.code)
|
||||
p.Print(c.text)
|
||||
|
||||
line, _ := rb.ReadString('\n')
|
||||
if line != c.text {
|
||||
t.Errorf("Expecting %s, got '%s'\n", c.text, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoColor_Env(t *testing.T) {
|
||||
rb := new(bytes.Buffer)
|
||||
Output = rb
|
||||
|
||||
testColors := []struct {
|
||||
text string
|
||||
code Attribute
|
||||
}{
|
||||
{text: "black", code: FgBlack},
|
||||
{text: "red", code: FgRed},
|
||||
{text: "green", code: FgGreen},
|
||||
{text: "yellow", code: FgYellow},
|
||||
{text: "blue", code: FgBlue},
|
||||
{text: "magent", code: FgMagenta},
|
||||
{text: "cyan", code: FgCyan},
|
||||
{text: "white", code: FgWhite},
|
||||
{text: "hblack", code: FgHiBlack},
|
||||
{text: "hred", code: FgHiRed},
|
||||
{text: "hgreen", code: FgHiGreen},
|
||||
{text: "hyellow", code: FgHiYellow},
|
||||
{text: "hblue", code: FgHiBlue},
|
||||
{text: "hmagent", code: FgHiMagenta},
|
||||
{text: "hcyan", code: FgHiCyan},
|
||||
{text: "hwhite", code: FgHiWhite},
|
||||
}
|
||||
|
||||
os.Setenv("NO_COLOR", "1")
|
||||
t.Cleanup(func() {
|
||||
os.Unsetenv("NO_COLOR")
|
||||
})
|
||||
|
||||
for _, c := range testColors {
|
||||
p := New(c.code)
|
||||
p.Print(c.text)
|
||||
|
||||
line, _ := rb.ReadString('\n')
|
||||
if line != c.text {
|
||||
t.Errorf("Expecting %s, got '%s'\n", c.text, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_noColorIsSet(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
act func()
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "default",
|
||||
act: func() {},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "NO_COLOR=1",
|
||||
act: func() { os.Setenv("NO_COLOR", "1") },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "NO_COLOR=",
|
||||
act: func() { os.Setenv("NO_COLOR", "") },
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
os.Unsetenv("NO_COLOR")
|
||||
})
|
||||
tt.act()
|
||||
if got := noColorIsSet(); got != tt.want {
|
||||
t.Errorf("noColorIsSet() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStdoutIsTerminal_NilStdout(t *testing.T) {
|
||||
stdout := os.Stdout
|
||||
os.Stdout = nil
|
||||
t.Cleanup(func() {
|
||||
os.Stdout = stdout
|
||||
})
|
||||
|
||||
if stdoutIsTerminal() {
|
||||
t.Fatal("stdoutIsTerminal() = true, want false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStdOut_NilStdout(t *testing.T) {
|
||||
stdout := os.Stdout
|
||||
os.Stdout = nil
|
||||
t.Cleanup(func() {
|
||||
os.Stdout = stdout
|
||||
})
|
||||
|
||||
if got := stdOut(); got != io.Discard {
|
||||
t.Fatalf("stdOut() = %v, want %v", got, io.Discard)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStdErr_NilStderr(t *testing.T) {
|
||||
stderr := os.Stderr
|
||||
os.Stderr = nil
|
||||
t.Cleanup(func() {
|
||||
os.Stderr = stderr
|
||||
})
|
||||
|
||||
if got := stdErr(); got != io.Discard {
|
||||
t.Fatalf("stdErr() = %v, want %v", got, io.Discard)
|
||||
}
|
||||
}
|
||||
|
||||
func TestColorVisual(t *testing.T) {
|
||||
// First Visual Test
|
||||
Output = colorable.NewColorableStdout()
|
||||
|
||||
New(FgRed).Printf("red\t")
|
||||
New(BgRed).Print(" ")
|
||||
New(FgRed, Bold).Println(" red")
|
||||
|
||||
New(FgGreen).Printf("green\t")
|
||||
New(BgGreen).Print(" ")
|
||||
New(FgGreen, Bold).Println(" green")
|
||||
|
||||
New(FgYellow).Printf("yellow\t")
|
||||
New(BgYellow).Print(" ")
|
||||
New(FgYellow, Bold).Println(" yellow")
|
||||
|
||||
New(FgBlue).Printf("blue\t")
|
||||
New(BgBlue).Print(" ")
|
||||
New(FgBlue, Bold).Println(" blue")
|
||||
|
||||
New(FgMagenta).Printf("magenta\t")
|
||||
New(BgMagenta).Print(" ")
|
||||
New(FgMagenta, Bold).Println(" magenta")
|
||||
|
||||
New(FgCyan).Printf("cyan\t")
|
||||
New(BgCyan).Print(" ")
|
||||
New(FgCyan, Bold).Println(" cyan")
|
||||
|
||||
New(FgWhite).Printf("white\t")
|
||||
New(BgWhite).Print(" ")
|
||||
New(FgWhite, Bold).Println(" white")
|
||||
fmt.Println("")
|
||||
|
||||
// Second Visual test
|
||||
Black("black")
|
||||
Red("red")
|
||||
Green("green")
|
||||
Yellow("yellow")
|
||||
Blue("blue")
|
||||
Magenta("magenta")
|
||||
Cyan("cyan")
|
||||
White("white")
|
||||
HiBlack("hblack")
|
||||
HiRed("hred")
|
||||
HiGreen("hgreen")
|
||||
HiYellow("hyellow")
|
||||
HiBlue("hblue")
|
||||
HiMagenta("hmagenta")
|
||||
HiCyan("hcyan")
|
||||
HiWhite("hwhite")
|
||||
|
||||
// Third visual test
|
||||
fmt.Println()
|
||||
Set(FgBlue)
|
||||
fmt.Println("is this blue?")
|
||||
Unset()
|
||||
|
||||
Set(FgMagenta)
|
||||
fmt.Println("and this magenta?")
|
||||
Unset()
|
||||
|
||||
// Fourth Visual test
|
||||
fmt.Println()
|
||||
blue := New(FgBlue).PrintlnFunc()
|
||||
blue("blue text with custom print func")
|
||||
|
||||
red := New(FgRed).PrintfFunc()
|
||||
red("red text with a printf func: %d\n", 123)
|
||||
|
||||
put := New(FgYellow).SprintFunc()
|
||||
warn := New(FgRed).SprintFunc()
|
||||
|
||||
fmt.Fprintf(Output, "this is a %s and this is %s.\n", put("warning"), warn("error"))
|
||||
|
||||
info := New(FgWhite, BgGreen).SprintFunc()
|
||||
fmt.Fprintf(Output, "this %s rocks!\n", info("package"))
|
||||
|
||||
notice := New(FgBlue).FprintFunc()
|
||||
notice(os.Stderr, "just a blue notice to stderr")
|
||||
|
||||
// Fifth Visual Test
|
||||
fmt.Println()
|
||||
|
||||
fmt.Fprintln(Output, BlackString("black"))
|
||||
fmt.Fprintln(Output, RedString("red"))
|
||||
fmt.Fprintln(Output, GreenString("green"))
|
||||
fmt.Fprintln(Output, YellowString("yellow"))
|
||||
fmt.Fprintln(Output, BlueString("blue"))
|
||||
fmt.Fprintln(Output, MagentaString("magenta"))
|
||||
fmt.Fprintln(Output, CyanString("cyan"))
|
||||
fmt.Fprintln(Output, WhiteString("white"))
|
||||
fmt.Fprintln(Output, HiBlackString("hblack"))
|
||||
fmt.Fprintln(Output, HiRedString("hred"))
|
||||
fmt.Fprintln(Output, HiGreenString("hgreen"))
|
||||
fmt.Fprintln(Output, HiYellowString("hyellow"))
|
||||
fmt.Fprintln(Output, HiBlueString("hblue"))
|
||||
fmt.Fprintln(Output, HiMagentaString("hmagenta"))
|
||||
fmt.Fprintln(Output, HiCyanString("hcyan"))
|
||||
fmt.Fprintln(Output, HiWhiteString("hwhite"))
|
||||
}
|
||||
|
||||
func TestNoFormat(t *testing.T) {
|
||||
fmt.Printf("%s %%s = ", BlackString("Black"))
|
||||
Black("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", RedString("Red"))
|
||||
Red("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", GreenString("Green"))
|
||||
Green("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", YellowString("Yellow"))
|
||||
Yellow("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", BlueString("Blue"))
|
||||
Blue("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", MagentaString("Magenta"))
|
||||
Magenta("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", CyanString("Cyan"))
|
||||
Cyan("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", WhiteString("White"))
|
||||
White("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", HiBlackString("HiBlack"))
|
||||
HiBlack("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", HiRedString("HiRed"))
|
||||
HiRed("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", HiGreenString("HiGreen"))
|
||||
HiGreen("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", HiYellowString("HiYellow"))
|
||||
HiYellow("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", HiBlueString("HiBlue"))
|
||||
HiBlue("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", HiMagentaString("HiMagenta"))
|
||||
HiMagenta("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", HiCyanString("HiCyan"))
|
||||
HiCyan("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", HiWhiteString("HiWhite"))
|
||||
HiWhite("%s")
|
||||
}
|
||||
|
||||
func TestNoFormatString(t *testing.T) {
|
||||
tests := []struct {
|
||||
f func(string, ...interface{}) string
|
||||
format string
|
||||
args []interface{}
|
||||
want string
|
||||
}{
|
||||
{BlackString, "%s", nil, "\x1b[30m%s\x1b[0m"},
|
||||
{RedString, "%s", nil, "\x1b[31m%s\x1b[0m"},
|
||||
{GreenString, "%s", nil, "\x1b[32m%s\x1b[0m"},
|
||||
{YellowString, "%s", nil, "\x1b[33m%s\x1b[0m"},
|
||||
{BlueString, "%s", nil, "\x1b[34m%s\x1b[0m"},
|
||||
{MagentaString, "%s", nil, "\x1b[35m%s\x1b[0m"},
|
||||
{CyanString, "%s", nil, "\x1b[36m%s\x1b[0m"},
|
||||
{WhiteString, "%s", nil, "\x1b[37m%s\x1b[0m"},
|
||||
{HiBlackString, "%s", nil, "\x1b[90m%s\x1b[0m"},
|
||||
{HiRedString, "%s", nil, "\x1b[91m%s\x1b[0m"},
|
||||
{HiGreenString, "%s", nil, "\x1b[92m%s\x1b[0m"},
|
||||
{HiYellowString, "%s", nil, "\x1b[93m%s\x1b[0m"},
|
||||
{HiBlueString, "%s", nil, "\x1b[94m%s\x1b[0m"},
|
||||
{HiMagentaString, "%s", nil, "\x1b[95m%s\x1b[0m"},
|
||||
{HiCyanString, "%s", nil, "\x1b[96m%s\x1b[0m"},
|
||||
{HiWhiteString, "%s", nil, "\x1b[97m%s\x1b[0m"},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
s := test.f(test.format, test.args...)
|
||||
|
||||
if s != test.want {
|
||||
t.Errorf("[%d] want: %q, got: %q", i, test.want, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestColor_Println_Newline(t *testing.T) {
|
||||
rb := new(bytes.Buffer)
|
||||
Output = rb
|
||||
|
||||
c := New(FgRed)
|
||||
c.Println("foo")
|
||||
|
||||
got := readRaw(t, rb)
|
||||
want := "\x1b[31mfoo\x1b[0m\n"
|
||||
|
||||
if want != got {
|
||||
t.Errorf("Println newline error\n\nwant: %q\n got: %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestColor_Sprintln_Newline(t *testing.T) {
|
||||
c := New(FgRed)
|
||||
|
||||
got := c.Sprintln("foo")
|
||||
want := "\x1b[31mfoo\x1b[0m\n"
|
||||
|
||||
if want != got {
|
||||
t.Errorf("Println newline error\n\nwant: %q\n got: %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestColor_Fprint(t *testing.T) {
|
||||
rb := new(strings.Builder)
|
||||
c := New(FgRed)
|
||||
|
||||
n, err := c.Fprint(rb, "foo", "bar")
|
||||
if err != nil {
|
||||
t.Errorf("Fprint error: %v", err)
|
||||
}
|
||||
got := rb.String()
|
||||
want := "\x1b[31mfoobar\x1b[0m"
|
||||
|
||||
if want != got {
|
||||
t.Errorf("Fprint error\n\nwant: %q\n got: %q", want, got)
|
||||
}
|
||||
if n != len(got) {
|
||||
t.Errorf("Fprint byte count does not match actual bytes written\n\nwant: %d\n got: %d", len(got), n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestColor_Fprintln(t *testing.T) {
|
||||
rb := new(strings.Builder)
|
||||
c := New(FgRed)
|
||||
|
||||
n, err := c.Fprintln(rb, "foo", "bar")
|
||||
if err != nil {
|
||||
t.Errorf("Fprint error: %v", err)
|
||||
}
|
||||
got := rb.String()
|
||||
want := "\x1b[31mfoo bar\x1b[0m\n"
|
||||
|
||||
if want != got {
|
||||
t.Errorf("Fprintln error\n\nwant: %q\n got: %q", want, got)
|
||||
}
|
||||
if n != len(got) {
|
||||
t.Errorf("Fprintln byte count does not match actual bytes written\n\nwant: %d\n got: %d", len(got), n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestColor_Fprintf(t *testing.T) {
|
||||
rb := new(strings.Builder)
|
||||
c := New(FgRed)
|
||||
|
||||
n, err := c.Fprintf(rb, "%-7s %-7s %5d\n", "hello", "world", 123)
|
||||
if err != nil {
|
||||
t.Errorf("Fprint error: %v", err)
|
||||
}
|
||||
|
||||
want := "\x1b[31mhello world 123\n\x1b[0m"
|
||||
|
||||
got := rb.String()
|
||||
if want != got {
|
||||
t.Errorf("Fprintf error\n\nwant: %q\n got: %q", want, got)
|
||||
}
|
||||
if n != len(got) {
|
||||
t.Errorf("Fprintf byte count does not match actual bytes written\n\nwant: %d\n got: %d", len(got), n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestColor_Fprintln_Newline(t *testing.T) {
|
||||
rb := new(bytes.Buffer)
|
||||
c := New(FgRed)
|
||||
c.Fprintln(rb, "foo")
|
||||
|
||||
got := readRaw(t, rb)
|
||||
want := "\x1b[31mfoo\x1b[0m\n"
|
||||
|
||||
if want != got {
|
||||
t.Errorf("Println newline error\n\nwant: %q\n got: %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func readRaw(t *testing.T, r io.Reader) string {
|
||||
t.Helper()
|
||||
|
||||
out, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return string(out)
|
||||
}
|
||||
|
||||
func TestIssue206_1(t *testing.T) {
|
||||
// visual test, go test -v .
|
||||
// to see the string with escape codes, use go test -v . > c:\temp\test.txt
|
||||
underline := New(Underline).Sprint
|
||||
|
||||
line := fmt.Sprintf("%s %s %s %s", "word1", underline("word2"), "word3", underline("word4"))
|
||||
|
||||
line = CyanString(line)
|
||||
|
||||
fmt.Println(line)
|
||||
|
||||
result := fmt.Sprintf("%v", line)
|
||||
const expectedResult = "\x1b[36mword1 \x1b[4mword2\x1b[24m word3 \x1b[4mword4\x1b[24m\x1b[0m"
|
||||
|
||||
if !bytes.Equal([]byte(result), []byte(expectedResult)) {
|
||||
t.Errorf("Expecting %v, got '%v'\n", expectedResult, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue206_2(t *testing.T) {
|
||||
underline := New(Underline).Sprint
|
||||
bold := New(Bold).Sprint
|
||||
|
||||
line := fmt.Sprintf("%s %s", GreenString(underline("underlined regular green")), RedString(bold("bold red")))
|
||||
|
||||
fmt.Println(line)
|
||||
|
||||
result := fmt.Sprintf("%v", line)
|
||||
const expectedResult = "\x1b[32m\x1b[4munderlined regular green\x1b[24m\x1b[0m \x1b[31m\x1b[1mbold red\x1b[22m\x1b[0m"
|
||||
|
||||
if !bytes.Equal([]byte(result), []byte(expectedResult)) {
|
||||
t.Errorf("Expecting %v, got '%v'\n", expectedResult, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue218(t *testing.T) {
|
||||
// Adds a newline to the end of the last string to make sure it isn't trimmed.
|
||||
params := []interface{}{"word1", "word2", "word3", "word4\n"}
|
||||
|
||||
c := New(FgCyan)
|
||||
c.Println(params...)
|
||||
|
||||
result := c.Sprintln(params...)
|
||||
fmt.Println(params...)
|
||||
fmt.Print(result)
|
||||
|
||||
const expectedResult = "\x1b[36mword1 word2 word3 word4\n\x1b[0m\n"
|
||||
|
||||
if !bytes.Equal([]byte(result), []byte(expectedResult)) {
|
||||
t.Errorf("Sprintln: Expecting %v (%v), got '%v (%v)'\n", expectedResult, []byte(expectedResult), result, []byte(result))
|
||||
}
|
||||
|
||||
fn := c.SprintlnFunc()
|
||||
result = fn(params...)
|
||||
if !bytes.Equal([]byte(result), []byte(expectedResult)) {
|
||||
t.Errorf("SprintlnFunc: Expecting %v (%v), got '%v (%v)'\n", expectedResult, []byte(expectedResult), result, []byte(result))
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
c.Fprintln(&buf, params...)
|
||||
result = buf.String()
|
||||
if !bytes.Equal([]byte(result), []byte(expectedResult)) {
|
||||
t.Errorf("Fprintln: Expecting %v (%v), got '%v (%v)'\n", expectedResult, []byte(expectedResult), result, []byte(result))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRGB(t *testing.T) {
|
||||
tests := []struct {
|
||||
r, g, b int
|
||||
}{
|
||||
{255, 128, 0}, // orange
|
||||
{230, 42, 42}, // red
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
RGB(tt.r, tt.g, tt.b).Println("foreground")
|
||||
RGB(tt.r, tt.g, tt.b).AddBgRGB(0, 0, 0).Println("with background")
|
||||
BgRGB(tt.r, tt.g, tt.b).Println("background")
|
||||
BgRGB(tt.r, tt.g, tt.b).AddRGB(255, 255, 255).Println("with foreground")
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user