// Copyright 2014 beego Author. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // +build windows package logs import ( "bytes" "fmt" "syscall" "testing" ) var GetConsoleScreenBufferInfo = getConsoleScreenBufferInfo func ChangeColor(color uint16) { setConsoleTextAttribute(uintptr(syscall.Stdout), color) } func ResetColor() { ChangeColor(uint16(0x0007)) } func TestWritePlanText(t *testing.T) { inner := bytes.NewBufferString("") w := NewAnsiColorWriter(inner) expected := "plain text" fmt.Fprintf(w, expected) actual := inner.String() if actual != expected { t.Errorf("Get %q, want %q", actual, expected) } } func TestWriteParseText(t *testing.T) { inner := bytes.NewBufferString("") w := NewAnsiColorWriter(inner) inputTail := "\x1b[0mtail text" expectedTail := "tail text" fmt.Fprintf(w, inputTail) actualTail := inner.String() inner.Reset() if actualTail != expectedTail { t.Errorf("Get %q, want %q", actualTail, expectedTail) } inputHead := "head text\x1b[0m" expectedHead := "head text" fmt.Fprintf(w, inputHead) actualHead := inner.String() inner.Reset() if actualHead != expectedHead { t.Errorf("Get %q, want %q", actualHead, expectedHead) } inputBothEnds := "both ends \x1b[0m text" expectedBothEnds := "both ends text" fmt.Fprintf(w, inputBothEnds) actualBothEnds := inner.String() inner.Reset() if actualBothEnds != expectedBothEnds { t.Errorf("Get %q, want %q", actualBothEnds, expectedBothEnds) } inputManyEsc := "\x1b\x1b\x1b\x1b[0m many esc" expectedManyEsc := "\x1b\x1b\x1b many esc" fmt.Fprintf(w, inputManyEsc) actualManyEsc := inner.String() inner.Reset() if actualManyEsc != expectedManyEsc { t.Errorf("Get %q, want %q", actualManyEsc, expectedManyEsc) } expectedSplit := "split text" for _, ch := range "split \x1b[0m text" { fmt.Fprintf(w, string(ch)) } actualSplit := inner.String() inner.Reset() if actualSplit != expectedSplit { t.Errorf("Get %q, want %q", actualSplit, expectedSplit) } } type screenNotFoundError struct { error } func writeAnsiColor(expectedText, colorCode string) (actualText string, actualAttributes uint16, err error) { inner := bytes.NewBufferString("") w := NewAnsiColorWriter(inner) fmt.Fprintf(w, "\x1b[%sm%s", colorCode, expectedText) actualText = inner.String() screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout)) if screenInfo != nil { actualAttributes = screenInfo.WAttributes } else { err = &screenNotFoundError{} } return } type testParam struct { text string attributes uint16 ansiColor string } func TestWriteAnsiColorText(t *testing.T) { screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout)) if screenInfo == nil { t.Fatal("Could not get ConsoleScreenBufferInfo") } defer ChangeColor(screenInfo.WAttributes) defaultFgColor := screenInfo.WAttributes & uint16(0x0007) defaultBgColor := screenInfo.WAttributes & uint16(0x0070) defaultFgIntensity := screenInfo.WAttributes & uint16(0x0008) defaultBgIntensity := screenInfo.WAttributes & uint16(0x0080) fgParam := []testParam{ {"foreground black ", uint16(0x0000 | 0x0000), "30"}, {"foreground red ", uint16(0x0004 | 0x0000), "31"}, {"foreground green ", uint16(0x0002 | 0x0000), "32"}, {"foreground yellow ", uint16(0x0006 | 0x0000), "33"}, {"foreground blue ", uint16(0x0001 | 0x0000), "34"}, {"foreground magenta", uint16(0x0005 | 0x0000), "35"}, {"foreground cyan ", uint16(0x0003 | 0x0000), "36"}, {"foreground white ", uint16(0x0007 | 0x0000), "37"}, {"foreground default", defaultFgColor | 0x0000, "39"}, {"foreground light gray ", uint16(0x0000 | 0x0008 | 0x0000), "90"}, {"foreground light red ", uint16(0x0004 | 0x0008 | 0x0000), "91"}, {"foreground light green ", uint16(0x0002 | 0x0008 | 0x0000), "92"}, {"foreground light yellow ", uint16(0x0006 | 0x0008 | 0x0000), "93"}, {"foreground light blue ", uint16(0x0001 | 0x0008 | 0x0000), "94"}, {"foreground light magenta", uint16(0x0005 | 0x0008 | 0x0000), "95"}, {"foreground light cyan ", uint16(0x0003 | 0x0008 | 0x0000), "96"}, {"foreground light white ", uint16(0x0007 | 0x0008 | 0x0000), "97"}, } bgParam := []testParam{ {"background black ", uint16(0x0007 | 0x0000), "40"}, {"background red ", uint16(0x0007 | 0x0040), "41"}, {"background green ", uint16(0x0007 | 0x0020), "42"}, {"background yellow ", uint16(0x0007 | 0x0060), "43"}, {"background blue ", uint16(0x0007 | 0x0010), "44"}, {"background magenta", uint16(0x0007 | 0x0050), "45"}, {"background cyan ", uint16(0x0007 | 0x0030), "46"}, {"background white ", uint16(0x0007 | 0x0070), "47"}, {"background default", uint16(0x0007) | defaultBgColor, "49"}, {"background light gray ", uint16(0x0007 | 0x0000 | 0x0080), "100"}, {"background light red ", uint16(0x0007 | 0x0040 | 0x0080), "101"}, {"background light green ", uint16(0x0007 | 0x0020 | 0x0080), "102"}, {"background light yellow ", uint16(0x0007 | 0x0060 | 0x0080), "103"}, {"background light blue ", uint16(0x0007 | 0x0010 | 0x0080), "104"}, {"background light magenta", uint16(0x0007 | 0x0050 | 0x0080), "105"}, {"background light cyan ", uint16(0x0007 | 0x0030 | 0x0080), "106"}, {"background light white ", uint16(0x0007 | 0x0070 | 0x0080), "107"}, } resetParam := []testParam{ {"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, "0"}, {"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, ""}, } boldParam := []testParam{ {"bold on", uint16(0x0007 | 0x0008), "1"}, {"bold off", uint16(0x0007), "21"}, } underscoreParam := []testParam{ {"underscore on", uint16(0x0007 | 0x8000), "4"}, {"underscore off", uint16(0x0007), "24"}, } blinkParam := []testParam{ {"blink on", uint16(0x0007 | 0x0080), "5"}, {"blink off", uint16(0x0007), "25"}, } mixedParam := []testParam{ {"both black, bold, underline, blink", uint16(0x0000 | 0x0000 | 0x0008 | 0x8000 | 0x0080), "30;40;1;4;5"}, {"both red, bold, underline, blink", uint16(0x0004 | 0x0040 | 0x0008 | 0x8000 | 0x0080), "31;41;1;4;5"}, {"both green, bold, underline, blink", uint16(0x0002 | 0x0020 | 0x0008 | 0x8000 | 0x0080), "32;42;1;4;5"}, {"both yellow, bold, underline, blink", uint16(0x0006 | 0x0060 | 0x0008 | 0x8000 | 0x0080), "33;43;1;4;5"}, {"both blue, bold, underline, blink", uint16(0x0001 | 0x0010 | 0x0008 | 0x8000 | 0x0080), "34;44;1;4;5"}, {"both magenta, bold, underline, blink", uint16(0x0005 | 0x0050 | 0x0008 | 0x8000 | 0x0080), "35;45;1;4;5"}, {"both cyan, bold, underline, blink", uint16(0x0003 | 0x0030 | 0x0008 | 0x8000 | 0x0080), "36;46;1;4;5"}, {"both white, bold, underline, blink", uint16(0x0007 | 0x0070 | 0x0008 | 0x8000 | 0x0080), "37;47;1;4;5"}, {"both default, bold, underline, blink", uint16(defaultFgColor | defaultBgColor | 0x0008 | 0x8000 | 0x0080), "39;49;1;4;5"}, } assertTextAttribute := func(expectedText string, expectedAttributes uint16, ansiColor string) { actualText, actualAttributes, err := writeAnsiColor(expectedText, ansiColor) if actualText != expectedText { t.Errorf("Get %q, want %q", actualText, expectedText) } if err != nil { t.Fatal("Could not get ConsoleScreenBufferInfo") } if actualAttributes != expectedAttributes { t.Errorf("Text: %q, Get 0x%04x, want 0x%04x", expectedText, actualAttributes, expectedAttributes) } } for _, v := range fgParam { ResetColor() assertTextAttribute(v.text, v.attributes, v.ansiColor) } for _, v := range bgParam { ChangeColor(uint16(0x0070 | 0x0007)) assertTextAttribute(v.text, v.attributes, v.ansiColor) } for _, v := range resetParam { ChangeColor(uint16(0x0000 | 0x0070 | 0x0008)) assertTextAttribute(v.text, v.attributes, v.ansiColor) } ResetColor() for _, v := range boldParam { assertTextAttribute(v.text, v.attributes, v.ansiColor) } ResetColor() for _, v := range underscoreParam { assertTextAttribute(v.text, v.attributes, v.ansiColor) } ResetColor() for _, v := range blinkParam { assertTextAttribute(v.text, v.attributes, v.ansiColor) } for _, v := range mixedParam { ResetColor() assertTextAttribute(v.text, v.attributes, v.ansiColor) } } func TestIgnoreUnknownSequences(t *testing.T) { inner := bytes.NewBufferString("") w := NewModeAnsiColorWriter(inner, OutputNonColorEscSeq) inputText := "\x1b[=decpath mode" expectedTail := inputText fmt.Fprintf(w, inputText) actualTail := inner.String() inner.Reset() if actualTail != expectedTail { t.Errorf("Get %q, want %q", actualTail, expectedTail) } inputText = "\x1b[=tailing esc and bracket\x1b[" expectedTail = inputText fmt.Fprintf(w, inputText) actualTail = inner.String() inner.Reset() if actualTail != expectedTail { t.Errorf("Get %q, want %q", actualTail, expectedTail) } inputText = "\x1b[?tailing esc\x1b" expectedTail = inputText fmt.Fprintf(w, inputText) actualTail = inner.String() inner.Reset() if actualTail != expectedTail { t.Errorf("Get %q, want %q", actualTail, expectedTail) } inputText = "\x1b[1h;3punended color code invalid\x1b3" expectedTail = inputText fmt.Fprintf(w, inputText) actualTail = inner.String() inner.Reset() if actualTail != expectedTail { t.Errorf("Get %q, want %q", actualTail, expectedTail) } }