// Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package cover import ( "fmt" "io/ioutil" "os" "reflect" "testing" ) func TestParseProfiles(t *testing.T) { tests := []struct { name string input string output []*Profile expectErr bool }{ { name: "parsing an empty file produces empty output", input: `mode: set`, output: []*Profile{}, }, { name: "simple valid file produces expected output", input: `mode: set some/fancy/path:42.69,44.16 2 1`, output: []*Profile{ { FileName: "some/fancy/path", Mode: "set", Blocks: []ProfileBlock{ { StartLine: 42, StartCol: 69, EndLine: 44, EndCol: 16, NumStmt: 2, Count: 1, }, }, }, }, }, { name: "file with syntax characters in path produces expected output", input: `mode: set some fancy:path/some,file.go:42.69,44.16 2 1`, output: []*Profile{ { FileName: "some fancy:path/some,file.go", Mode: "set", Blocks: []ProfileBlock{ { StartLine: 42, StartCol: 69, EndLine: 44, EndCol: 16, NumStmt: 2, Count: 1, }, }, }, }, }, { name: "file with multiple blocks in one file produces expected output", input: `mode: set some/fancy/path:42.69,44.16 2 1 some/fancy/path:44.16,46.3 1 0`, output: []*Profile{ { FileName: "some/fancy/path", Mode: "set", Blocks: []ProfileBlock{ { StartLine: 42, StartCol: 69, EndLine: 44, EndCol: 16, NumStmt: 2, Count: 1, }, { StartLine: 44, StartCol: 16, EndLine: 46, EndCol: 3, NumStmt: 1, Count: 0, }, }, }, }, }, { name: "file with multiple files produces expected output", input: `mode: set another/fancy/path:44.16,46.3 1 0 some/fancy/path:42.69,44.16 2 1`, output: []*Profile{ { FileName: "another/fancy/path", Mode: "set", Blocks: []ProfileBlock{ { StartLine: 44, StartCol: 16, EndLine: 46, EndCol: 3, NumStmt: 1, Count: 0, }, }, }, { FileName: "some/fancy/path", Mode: "set", Blocks: []ProfileBlock{ { StartLine: 42, StartCol: 69, EndLine: 44, EndCol: 16, NumStmt: 2, Count: 1, }, }, }, }, }, { name: "intertwined files are merged correctly", input: `mode: set some/fancy/path:42.69,44.16 2 1 another/fancy/path:47.2,47.13 1 1 some/fancy/path:44.16,46.3 1 0`, output: []*Profile{ { FileName: "another/fancy/path", Mode: "set", Blocks: []ProfileBlock{ { StartLine: 47, StartCol: 2, EndLine: 47, EndCol: 13, NumStmt: 1, Count: 1, }, }, }, { FileName: "some/fancy/path", Mode: "set", Blocks: []ProfileBlock{ { StartLine: 42, StartCol: 69, EndLine: 44, EndCol: 16, NumStmt: 2, Count: 1, }, { StartLine: 44, StartCol: 16, EndLine: 46, EndCol: 3, NumStmt: 1, Count: 0, }, }, }, }, }, { name: "duplicate blocks are merged correctly", input: `mode: count some/fancy/path:42.69,44.16 2 4 some/fancy/path:42.69,44.16 2 3`, output: []*Profile{ { FileName: "some/fancy/path", Mode: "count", Blocks: []ProfileBlock{ { StartLine: 42, StartCol: 69, EndLine: 44, EndCol: 16, NumStmt: 2, Count: 7, }, }, }, }, }, { name: "an invalid mode line is an error", input: `mode:count`, expectErr: true, }, { name: "a missing field is an error", input: `mode: count some/fancy/path:42.69,44.16 2`, expectErr: true, }, { name: "a missing path field is an error", input: `mode: count 42.69,44.16 2 3`, expectErr: true, }, { name: "a non-numeric count is an error", input: `mode: count 42.69,44.16 2 nope`, expectErr: true, }, { name: "an empty path is an error", input: `mode: count :42.69,44.16 2 3`, expectErr: true, }, { name: "a negative count is an error", input: `mode: count some/fancy/path:42.69,44.16 2 -1`, expectErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { f, err := ioutil.TempFile("", "") if err != nil { t.Fatalf("Failed to create a temp file: %v.", err) } defer func() { f.Close() os.Remove(f.Name()) }() n, err := f.WriteString(tc.input) if err != nil { t.Fatalf("Failed to write to temp file: %v", err) } if n < len(tc.input) { t.Fatalf("Didn't write enough bytes to temp file (wrote %d, expected %d).", n, len(tc.input)) } if err := f.Sync(); err != nil { t.Fatalf("Failed to sync temp file: %v", err) } result, err := ParseProfiles(f.Name()) if err != nil { if !tc.expectErr { t.Errorf("Unexpected error: %v", err) } return } if tc.expectErr { t.Errorf("Expected an error, but got value %q", stringifyProfileArray(result)) } if !reflect.DeepEqual(result, tc.output) { t.Errorf("Mismatched results.\nExpected: %s\nActual: %s", stringifyProfileArray(tc.output), stringifyProfileArray(result)) } }) } } func stringifyProfileArray(profiles []*Profile) string { deref := make([]Profile, 0, len(profiles)) for _, p := range profiles { deref = append(deref, *p) } return fmt.Sprintf("%#v", deref) } func BenchmarkParseLine(b *testing.B) { const line = "k8s.io/kubernetes/cmd/kube-controller-manager/app/options/ttlafterfinishedcontroller.go:31.73,32.14 1 1" b.SetBytes(int64(len(line))) for n := 0; n < b.N; n++ { parseLine(line) } }