github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/cover/profile_test.go (about)

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package cover
     6  
     7  import (
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"reflect"
    12  	"testing"
    13  )
    14  
    15  func TestParseProfiles(t *testing.T) {
    16  	tests := []struct {
    17  		name      string
    18  		input     string
    19  		output    []*Profile
    20  		expectErr bool
    21  	}{
    22  		{
    23  			name:   "parsing an empty file produces empty output",
    24  			input:  `mode: set`,
    25  			output: []*Profile{},
    26  		},
    27  		{
    28  			name: "simple valid file produces expected output",
    29  			input: `mode: set
    30  some/fancy/path:42.69,44.16 2 1`,
    31  			output: []*Profile{
    32  				{
    33  					FileName: "some/fancy/path",
    34  					Mode:     "set",
    35  					Blocks: []ProfileBlock{
    36  						{
    37  							StartLine: 42, StartCol: 69,
    38  							EndLine: 44, EndCol: 16,
    39  							NumStmt: 2, Count: 1,
    40  						},
    41  					},
    42  				},
    43  			},
    44  		},
    45  		{
    46  			name: "file with syntax characters in path produces expected output",
    47  			input: `mode: set
    48  some fancy:path/some,file.go:42.69,44.16 2 1`,
    49  			output: []*Profile{
    50  				{
    51  					FileName: "some fancy:path/some,file.go",
    52  					Mode:     "set",
    53  					Blocks: []ProfileBlock{
    54  						{
    55  							StartLine: 42, StartCol: 69,
    56  							EndLine: 44, EndCol: 16,
    57  							NumStmt: 2, Count: 1,
    58  						},
    59  					},
    60  				},
    61  			},
    62  		},
    63  		{
    64  			name: "file with multiple blocks in one file produces expected output",
    65  			input: `mode: set
    66  some/fancy/path:42.69,44.16 2 1
    67  some/fancy/path:44.16,46.3 1 0`,
    68  			output: []*Profile{
    69  				{
    70  					FileName: "some/fancy/path",
    71  					Mode:     "set",
    72  					Blocks: []ProfileBlock{
    73  						{
    74  							StartLine: 42, StartCol: 69,
    75  							EndLine: 44, EndCol: 16,
    76  							NumStmt: 2, Count: 1,
    77  						},
    78  						{
    79  							StartLine: 44, StartCol: 16,
    80  							EndLine: 46, EndCol: 3,
    81  							NumStmt: 1, Count: 0,
    82  						},
    83  					},
    84  				},
    85  			},
    86  		},
    87  		{
    88  			name: "file with multiple files produces expected output",
    89  			input: `mode: set
    90  another/fancy/path:44.16,46.3 1 0
    91  some/fancy/path:42.69,44.16 2 1`,
    92  			output: []*Profile{
    93  				{
    94  					FileName: "another/fancy/path",
    95  					Mode:     "set",
    96  					Blocks: []ProfileBlock{
    97  						{
    98  							StartLine: 44, StartCol: 16,
    99  							EndLine: 46, EndCol: 3,
   100  							NumStmt: 1, Count: 0,
   101  						},
   102  					},
   103  				},
   104  				{
   105  					FileName: "some/fancy/path",
   106  					Mode:     "set",
   107  					Blocks: []ProfileBlock{
   108  						{
   109  							StartLine: 42, StartCol: 69,
   110  							EndLine: 44, EndCol: 16,
   111  							NumStmt: 2, Count: 1,
   112  						},
   113  					},
   114  				},
   115  			},
   116  		},
   117  		{
   118  			name: "intertwined files are merged correctly",
   119  			input: `mode: set
   120  some/fancy/path:42.69,44.16 2 1
   121  another/fancy/path:47.2,47.13 1 1
   122  some/fancy/path:44.16,46.3 1 0`,
   123  			output: []*Profile{
   124  				{
   125  					FileName: "another/fancy/path",
   126  					Mode:     "set",
   127  					Blocks: []ProfileBlock{
   128  						{
   129  							StartLine: 47, StartCol: 2,
   130  							EndLine: 47, EndCol: 13,
   131  							NumStmt: 1, Count: 1,
   132  						},
   133  					},
   134  				},
   135  				{
   136  					FileName: "some/fancy/path",
   137  					Mode:     "set",
   138  					Blocks: []ProfileBlock{
   139  						{
   140  							StartLine: 42, StartCol: 69,
   141  							EndLine: 44, EndCol: 16,
   142  							NumStmt: 2, Count: 1,
   143  						},
   144  						{
   145  							StartLine: 44, StartCol: 16,
   146  							EndLine: 46, EndCol: 3,
   147  							NumStmt: 1, Count: 0,
   148  						},
   149  					},
   150  				},
   151  			},
   152  		},
   153  		{
   154  			name: "duplicate blocks are merged correctly",
   155  			input: `mode: count
   156  some/fancy/path:42.69,44.16 2 4
   157  some/fancy/path:42.69,44.16 2 3`,
   158  			output: []*Profile{
   159  				{
   160  					FileName: "some/fancy/path",
   161  					Mode:     "count",
   162  					Blocks: []ProfileBlock{
   163  						{
   164  							StartLine: 42, StartCol: 69,
   165  							EndLine: 44, EndCol: 16,
   166  							NumStmt: 2, Count: 7,
   167  						},
   168  					},
   169  				},
   170  			},
   171  		},
   172  		{
   173  			name:      "an invalid mode line is an error",
   174  			input:     `mode:count`,
   175  			expectErr: true,
   176  		},
   177  		{
   178  			name: "a missing field is an error",
   179  			input: `mode: count
   180  some/fancy/path:42.69,44.16 2`,
   181  			expectErr: true,
   182  		},
   183  		{
   184  			name: "a missing path field is an error",
   185  			input: `mode: count
   186  42.69,44.16 2 3`,
   187  			expectErr: true,
   188  		},
   189  		{
   190  			name: "a non-numeric count is an error",
   191  			input: `mode: count
   192  42.69,44.16 2 nope`,
   193  			expectErr: true,
   194  		},
   195  		{
   196  			name: "an empty path is an error",
   197  			input: `mode: count
   198  :42.69,44.16 2 3`,
   199  			expectErr: true,
   200  		},
   201  		{
   202  			name: "a negative count is an error",
   203  			input: `mode: count
   204  some/fancy/path:42.69,44.16 2 -1`,
   205  			expectErr: true,
   206  		},
   207  	}
   208  
   209  	for _, tc := range tests {
   210  		t.Run(tc.name, func(t *testing.T) {
   211  			f, err := ioutil.TempFile("", "")
   212  			if err != nil {
   213  				t.Fatalf("Failed to create a temp file: %v.", err)
   214  			}
   215  			defer func() {
   216  				f.Close()
   217  				os.Remove(f.Name())
   218  			}()
   219  			n, err := f.WriteString(tc.input)
   220  			if err != nil {
   221  				t.Fatalf("Failed to write to temp file: %v", err)
   222  			}
   223  			if n < len(tc.input) {
   224  				t.Fatalf("Didn't write enough bytes to temp file (wrote %d, expected %d).", n, len(tc.input))
   225  			}
   226  			if err := f.Sync(); err != nil {
   227  				t.Fatalf("Failed to sync temp file: %v", err)
   228  			}
   229  
   230  			result, err := ParseProfiles(f.Name())
   231  			if err != nil {
   232  				if !tc.expectErr {
   233  					t.Errorf("Unexpected error: %v", err)
   234  				}
   235  				return
   236  			}
   237  			if tc.expectErr {
   238  				t.Errorf("Expected an error, but got value %q", stringifyProfileArray(result))
   239  			}
   240  			if !reflect.DeepEqual(result, tc.output) {
   241  				t.Errorf("Mismatched results.\nExpected: %s\nActual:   %s", stringifyProfileArray(tc.output), stringifyProfileArray(result))
   242  			}
   243  		})
   244  	}
   245  }
   246  
   247  func stringifyProfileArray(profiles []*Profile) string {
   248  	deref := make([]Profile, 0, len(profiles))
   249  	for _, p := range profiles {
   250  		deref = append(deref, *p)
   251  	}
   252  	return fmt.Sprintf("%#v", deref)
   253  }
   254  
   255  func BenchmarkParseLine(b *testing.B) {
   256  	const line = "k8s.io/kubernetes/cmd/kube-controller-manager/app/options/ttlafterfinishedcontroller.go:31.73,32.14 1 1"
   257  	b.SetBytes(int64(len(line)))
   258  	for n := 0; n < b.N; n++ {
   259  		parseLine(line)
   260  	}
   261  }