...

Source file src/github.com/google/pprof/profile/filter_test.go

Documentation: github.com/google/pprof/profile

     1  // Copyright 2018 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package profile
    16  
    17  import (
    18  	"fmt"
    19  	"regexp"
    20  	"strings"
    21  	"testing"
    22  
    23  	"github.com/google/pprof/internal/proftest"
    24  )
    25  
    26  var mappings = []*Mapping{
    27  	{ID: 1, Start: 0x10000, Limit: 0x40000, File: "map0", HasFunctions: true, HasFilenames: true, HasLineNumbers: true, HasInlineFrames: true},
    28  	{ID: 2, Start: 0x50000, Limit: 0x70000, File: "map1", HasFunctions: true, HasFilenames: true, HasLineNumbers: true, HasInlineFrames: true},
    29  }
    30  
    31  var functions = []*Function{
    32  	{ID: 1, Name: "fun0", SystemName: "fun0", Filename: "file0"},
    33  	{ID: 2, Name: "fun1", SystemName: "fun1", Filename: "file1"},
    34  	{ID: 3, Name: "fun2", SystemName: "fun2", Filename: "file2"},
    35  	{ID: 4, Name: "fun3", SystemName: "fun3", Filename: "file3"},
    36  	{ID: 5, Name: "fun4", SystemName: "fun4", Filename: "file4"},
    37  	{ID: 6, Name: "fun5", SystemName: "fun5", Filename: "file5"},
    38  	{ID: 7, Name: "fun6", SystemName: "fun6", Filename: "file6"},
    39  	{ID: 8, Name: "fun7", SystemName: "fun7", Filename: "file7"},
    40  	{ID: 9, Name: "fun8", SystemName: "fun8", Filename: "file8"},
    41  	{ID: 10, Name: "fun9", SystemName: "fun9", Filename: "file9"},
    42  	{ID: 11, Name: "fun10", SystemName: "fun10", Filename: "file10"},
    43  }
    44  
    45  var noInlinesLocs = []*Location{
    46  	{ID: 1, Mapping: mappings[0], Address: 0x1000, Line: []Line{{Function: functions[0], Line: 1}}},
    47  	{ID: 2, Mapping: mappings[0], Address: 0x2000, Line: []Line{{Function: functions[1], Line: 1}}},
    48  	{ID: 3, Mapping: mappings[0], Address: 0x3000, Line: []Line{{Function: functions[2], Line: 1}}},
    49  	{ID: 4, Mapping: mappings[0], Address: 0x4000, Line: []Line{{Function: functions[3], Line: 1}}},
    50  	{ID: 5, Mapping: mappings[0], Address: 0x5000, Line: []Line{{Function: functions[4], Line: 1}}},
    51  	{ID: 6, Mapping: mappings[0], Address: 0x6000, Line: []Line{{Function: functions[5], Line: 1}}},
    52  	{ID: 7, Mapping: mappings[0], Address: 0x7000, Line: []Line{{Function: functions[6], Line: 1}}},
    53  	{ID: 8, Mapping: mappings[0], Address: 0x8000, Line: []Line{{Function: functions[7], Line: 1}}},
    54  	{ID: 9, Mapping: mappings[0], Address: 0x9000, Line: []Line{{Function: functions[8], Line: 1}}},
    55  	{ID: 10, Mapping: mappings[0], Address: 0x10000, Line: []Line{{Function: functions[9], Line: 1}}},
    56  	{ID: 11, Mapping: mappings[1], Address: 0x11000, Line: []Line{{Function: functions[10], Line: 1}}},
    57  }
    58  
    59  var noInlinesProfile = &Profile{
    60  	TimeNanos:     10000,
    61  	PeriodType:    &ValueType{Type: "cpu", Unit: "milliseconds"},
    62  	Period:        1,
    63  	DurationNanos: 10e9,
    64  	SampleType:    []*ValueType{{Type: "samples", Unit: "count"}},
    65  	Mapping:       mappings,
    66  	Function:      functions,
    67  	Location:      noInlinesLocs,
    68  	Sample: []*Sample{
    69  		{Value: []int64{1}, Location: []*Location{noInlinesLocs[0], noInlinesLocs[1], noInlinesLocs[2], noInlinesLocs[3]}},
    70  		{Value: []int64{2}, Location: []*Location{noInlinesLocs[4], noInlinesLocs[5], noInlinesLocs[1], noInlinesLocs[6]}},
    71  		{Value: []int64{3}, Location: []*Location{noInlinesLocs[7], noInlinesLocs[8]}},
    72  		{Value: []int64{4}, Location: []*Location{noInlinesLocs[9], noInlinesLocs[4], noInlinesLocs[10], noInlinesLocs[7]}},
    73  	},
    74  }
    75  
    76  var allNoInlinesSampleFuncs = []string{
    77  	"fun0 fun1 fun2 fun3: 1",
    78  	"fun4 fun5 fun1 fun6: 2",
    79  	"fun7 fun8: 3",
    80  	"fun9 fun4 fun10 fun7: 4",
    81  }
    82  
    83  var inlinesLocs = []*Location{
    84  	{ID: 1, Mapping: mappings[0], Address: 0x1000, Line: []Line{{Function: functions[0], Line: 1}, {Function: functions[1], Line: 1}}},
    85  	{ID: 2, Mapping: mappings[0], Address: 0x2000, Line: []Line{{Function: functions[2], Line: 1}, {Function: functions[3], Line: 1}}},
    86  	{ID: 3, Mapping: mappings[0], Address: 0x3000, Line: []Line{{Function: functions[4], Line: 1}, {Function: functions[5], Line: 1}, {Function: functions[6], Line: 1}}},
    87  }
    88  
    89  var inlinesProfile = &Profile{
    90  	TimeNanos:     10000,
    91  	PeriodType:    &ValueType{Type: "cpu", Unit: "milliseconds"},
    92  	Period:        1,
    93  	DurationNanos: 10e9,
    94  	SampleType:    []*ValueType{{Type: "samples", Unit: "count"}},
    95  	Mapping:       mappings,
    96  	Function:      functions,
    97  	Location:      inlinesLocs,
    98  	Sample: []*Sample{
    99  		{Value: []int64{1}, Location: []*Location{inlinesLocs[0], inlinesLocs[1]}},
   100  		{Value: []int64{2}, Location: []*Location{inlinesLocs[2]}},
   101  	},
   102  }
   103  
   104  var emptyLinesLocs = []*Location{
   105  	{ID: 1, Mapping: mappings[0], Address: 0x1000, Line: []Line{{Function: functions[0], Line: 1}, {Function: functions[1], Line: 1}}},
   106  	{ID: 2, Mapping: mappings[0], Address: 0x2000, Line: []Line{}},
   107  	{ID: 3, Mapping: mappings[1], Address: 0x2000, Line: []Line{}},
   108  }
   109  
   110  var emptyLinesProfile = &Profile{
   111  	TimeNanos:     10000,
   112  	PeriodType:    &ValueType{Type: "cpu", Unit: "milliseconds"},
   113  	Period:        1,
   114  	DurationNanos: 10e9,
   115  	SampleType:    []*ValueType{{Type: "samples", Unit: "count"}},
   116  	Mapping:       mappings,
   117  	Function:      functions,
   118  	Location:      emptyLinesLocs,
   119  	Sample: []*Sample{
   120  		{Value: []int64{1}, Location: []*Location{emptyLinesLocs[0], emptyLinesLocs[1]}},
   121  		{Value: []int64{2}, Location: []*Location{emptyLinesLocs[2]}},
   122  		{Value: []int64{3}, Location: []*Location{}},
   123  	},
   124  }
   125  
   126  func TestFilterSamplesByName(t *testing.T) {
   127  	for _, tc := range []struct {
   128  		// name is the name of the test case.
   129  		name string
   130  		// profile is the profile that gets filtered.
   131  		profile *Profile
   132  		// These are the inputs to FilterSamplesByName().
   133  		focus, ignore, hide, show *regexp.Regexp
   134  		// want{F,I,S,H}m are expected return values from FilterSamplesByName.
   135  		wantFm, wantIm, wantSm, wantHm bool
   136  		// wantSampleFuncs contains expected stack functions and sample value after
   137  		// filtering, in the same order as in the profile. The format is as
   138  		// returned by sampleFuncs function below, which is "callee caller: <num>".
   139  		wantSampleFuncs []string
   140  	}{
   141  		// No Filters
   142  		{
   143  			name:            "empty filters keep all frames",
   144  			profile:         noInlinesProfile,
   145  			wantFm:          true,
   146  			wantSampleFuncs: allNoInlinesSampleFuncs,
   147  		},
   148  		// Focus
   149  		{
   150  			name:    "focus with no matches",
   151  			profile: noInlinesProfile,
   152  			focus:   regexp.MustCompile("unknown"),
   153  		},
   154  		{
   155  			name:    "focus matches function names",
   156  			profile: noInlinesProfile,
   157  			focus:   regexp.MustCompile("fun1"),
   158  			wantFm:  true,
   159  			wantSampleFuncs: []string{
   160  				"fun0 fun1 fun2 fun3: 1",
   161  				"fun4 fun5 fun1 fun6: 2",
   162  				"fun9 fun4 fun10 fun7: 4",
   163  			},
   164  		},
   165  		{
   166  			name:    "focus matches file names",
   167  			profile: noInlinesProfile,
   168  			focus:   regexp.MustCompile("file1"),
   169  			wantFm:  true,
   170  			wantSampleFuncs: []string{
   171  				"fun0 fun1 fun2 fun3: 1",
   172  				"fun4 fun5 fun1 fun6: 2",
   173  				"fun9 fun4 fun10 fun7: 4",
   174  			},
   175  		},
   176  		{
   177  			name:    "focus matches mapping names",
   178  			profile: noInlinesProfile,
   179  			focus:   regexp.MustCompile("map1"),
   180  			wantFm:  true,
   181  			wantSampleFuncs: []string{
   182  				"fun9 fun4 fun10 fun7: 4",
   183  			},
   184  		},
   185  		{
   186  			name:    "focus matches inline functions",
   187  			profile: inlinesProfile,
   188  			focus:   regexp.MustCompile("fun5"),
   189  			wantFm:  true,
   190  			wantSampleFuncs: []string{
   191  				"fun4 fun5 fun6: 2",
   192  			},
   193  		},
   194  		// Ignore
   195  		{
   196  			name:            "ignore with no matches matches all samples",
   197  			profile:         noInlinesProfile,
   198  			ignore:          regexp.MustCompile("unknown"),
   199  			wantFm:          true,
   200  			wantSampleFuncs: allNoInlinesSampleFuncs,
   201  		},
   202  		{
   203  			name:    "ignore matches function names",
   204  			profile: noInlinesProfile,
   205  			ignore:  regexp.MustCompile("fun1"),
   206  			wantFm:  true,
   207  			wantIm:  true,
   208  			wantSampleFuncs: []string{
   209  				"fun7 fun8: 3",
   210  			},
   211  		},
   212  		{
   213  			name:    "ignore matches file names",
   214  			profile: noInlinesProfile,
   215  			ignore:  regexp.MustCompile("file1"),
   216  			wantFm:  true,
   217  			wantIm:  true,
   218  			wantSampleFuncs: []string{
   219  				"fun7 fun8: 3",
   220  			},
   221  		},
   222  		{
   223  			name:    "ignore matches mapping names",
   224  			profile: noInlinesProfile,
   225  			ignore:  regexp.MustCompile("map1"),
   226  			wantFm:  true,
   227  			wantIm:  true,
   228  			wantSampleFuncs: []string{
   229  				"fun0 fun1 fun2 fun3: 1",
   230  				"fun4 fun5 fun1 fun6: 2",
   231  				"fun7 fun8: 3",
   232  			},
   233  		},
   234  		{
   235  			name:    "ignore matches inline functions",
   236  			profile: inlinesProfile,
   237  			ignore:  regexp.MustCompile("fun5"),
   238  			wantFm:  true,
   239  			wantIm:  true,
   240  			wantSampleFuncs: []string{
   241  				"fun0 fun1 fun2 fun3: 1",
   242  			},
   243  		},
   244  		// Show
   245  		{
   246  			name:    "show with no matches",
   247  			profile: noInlinesProfile,
   248  			show:    regexp.MustCompile("unknown"),
   249  			wantFm:  true,
   250  		},
   251  		{
   252  			name:    "show matches function names",
   253  			profile: noInlinesProfile,
   254  			show:    regexp.MustCompile("fun1|fun2"),
   255  			wantFm:  true,
   256  			wantSm:  true,
   257  			wantSampleFuncs: []string{
   258  				"fun1 fun2: 1",
   259  				"fun1: 2",
   260  				"fun10: 4",
   261  			},
   262  		},
   263  		{
   264  			name:    "show matches file names",
   265  			profile: noInlinesProfile,
   266  			show:    regexp.MustCompile("file1|file3"),
   267  			wantFm:  true,
   268  			wantSm:  true,
   269  			wantSampleFuncs: []string{
   270  				"fun1 fun3: 1",
   271  				"fun1: 2",
   272  				"fun10: 4",
   273  			},
   274  		},
   275  		{
   276  			name:    "show matches mapping names",
   277  			profile: noInlinesProfile,
   278  			show:    regexp.MustCompile("map1"),
   279  			wantFm:  true,
   280  			wantSm:  true,
   281  			wantSampleFuncs: []string{
   282  				"fun10: 4",
   283  			},
   284  		},
   285  		{
   286  			name:    "show matches inline functions",
   287  			profile: inlinesProfile,
   288  			show:    regexp.MustCompile("fun[03]"),
   289  			wantFm:  true,
   290  			wantSm:  true,
   291  			wantSampleFuncs: []string{
   292  				"fun0 fun3: 1",
   293  			},
   294  		},
   295  		{
   296  			name:    "show keeps all lines when matching both mapping and function",
   297  			profile: inlinesProfile,
   298  			show:    regexp.MustCompile("map0|fun5"),
   299  			wantFm:  true,
   300  			wantSm:  true,
   301  			wantSampleFuncs: []string{
   302  				"fun0 fun1 fun2 fun3: 1",
   303  				"fun4 fun5 fun6: 2",
   304  			},
   305  		},
   306  		// Hide
   307  		{
   308  			name:            "hide with no matches",
   309  			profile:         noInlinesProfile,
   310  			hide:            regexp.MustCompile("unknown"),
   311  			wantFm:          true,
   312  			wantSampleFuncs: allNoInlinesSampleFuncs,
   313  		},
   314  		{
   315  			name:    "hide matches function names",
   316  			profile: noInlinesProfile,
   317  			hide:    regexp.MustCompile("fun1|fun2"),
   318  			wantFm:  true,
   319  			wantHm:  true,
   320  			wantSampleFuncs: []string{
   321  				"fun0 fun3: 1",
   322  				"fun4 fun5 fun6: 2",
   323  				"fun7 fun8: 3",
   324  				"fun9 fun4 fun7: 4",
   325  			},
   326  		},
   327  		{
   328  			name:    "hide matches file names",
   329  			profile: noInlinesProfile,
   330  			hide:    regexp.MustCompile("file1|file3"),
   331  			wantFm:  true,
   332  			wantHm:  true,
   333  			wantSampleFuncs: []string{
   334  				"fun0 fun2: 1",
   335  				"fun4 fun5 fun6: 2",
   336  				"fun7 fun8: 3",
   337  				"fun9 fun4 fun7: 4",
   338  			},
   339  		},
   340  		{
   341  			name:    "hide matches mapping names",
   342  			profile: noInlinesProfile,
   343  			hide:    regexp.MustCompile("map1"),
   344  			wantFm:  true,
   345  			wantHm:  true,
   346  			wantSampleFuncs: []string{
   347  				"fun0 fun1 fun2 fun3: 1",
   348  				"fun4 fun5 fun1 fun6: 2",
   349  				"fun7 fun8: 3",
   350  				"fun9 fun4 fun7: 4",
   351  			},
   352  		},
   353  		{
   354  			name:    "hide matches inline functions",
   355  			profile: inlinesProfile,
   356  			hide:    regexp.MustCompile("fun[125]"),
   357  			wantFm:  true,
   358  			wantHm:  true,
   359  			wantSampleFuncs: []string{
   360  				"fun0 fun3: 1",
   361  				"fun4 fun6: 2",
   362  			},
   363  		},
   364  		{
   365  			name:    "hide drops all lines when matching both mapping and function",
   366  			profile: inlinesProfile,
   367  			hide:    regexp.MustCompile("map0|fun5"),
   368  			wantFm:  true,
   369  			wantHm:  true,
   370  		},
   371  		// Compound filters
   372  		{
   373  			name:    "hides a stack matched by both focus and ignore",
   374  			profile: noInlinesProfile,
   375  			focus:   regexp.MustCompile("fun1|fun7"),
   376  			ignore:  regexp.MustCompile("fun1"),
   377  			wantFm:  true,
   378  			wantIm:  true,
   379  			wantSampleFuncs: []string{
   380  				"fun7 fun8: 3",
   381  			},
   382  		},
   383  		{
   384  			name:    "hides a function if both show and hide match it",
   385  			profile: noInlinesProfile,
   386  			show:    regexp.MustCompile("fun1"),
   387  			hide:    regexp.MustCompile("fun10"),
   388  			wantFm:  true,
   389  			wantSm:  true,
   390  			wantHm:  true,
   391  			wantSampleFuncs: []string{
   392  				"fun1: 1",
   393  				"fun1: 2",
   394  			},
   395  		},
   396  	} {
   397  		t.Run(tc.name, func(t *testing.T) {
   398  			p := tc.profile.Copy()
   399  			fm, im, hm, sm := p.FilterSamplesByName(tc.focus, tc.ignore, tc.hide, tc.show)
   400  
   401  			type match struct{ fm, im, hm, sm bool }
   402  			if got, want := (match{fm: fm, im: im, hm: hm, sm: sm}), (match{fm: tc.wantFm, im: tc.wantIm, hm: tc.wantHm, sm: tc.wantSm}); got != want {
   403  				t.Errorf("match got %+v want %+v", got, want)
   404  			}
   405  
   406  			if got, want := strings.Join(sampleFuncs(p), "\n")+"\n", strings.Join(tc.wantSampleFuncs, "\n")+"\n"; got != want {
   407  				diff, err := proftest.Diff([]byte(want), []byte(got))
   408  				if err != nil {
   409  					t.Fatalf("failed to get diff: %v", err)
   410  				}
   411  				t.Errorf("FilterSamplesByName: got diff(want->got):\n%s", diff)
   412  			}
   413  		})
   414  	}
   415  }
   416  
   417  func TestShowFrom(t *testing.T) {
   418  	for _, tc := range []struct {
   419  		name     string
   420  		profile  *Profile
   421  		showFrom *regexp.Regexp
   422  		// wantMatch is the expected return value.
   423  		wantMatch bool
   424  		// wantSampleFuncs contains expected stack functions and sample value after
   425  		// filtering, in the same order as in the profile. The format is as
   426  		// returned by sampleFuncs function below, which is "callee caller: <num>".
   427  		wantSampleFuncs []string
   428  	}{
   429  		{
   430  			name:            "nil showFrom keeps all frames",
   431  			profile:         noInlinesProfile,
   432  			wantMatch:       false,
   433  			wantSampleFuncs: allNoInlinesSampleFuncs,
   434  		},
   435  		{
   436  			name:      "showFrom with no matches drops all samples",
   437  			profile:   noInlinesProfile,
   438  			showFrom:  regexp.MustCompile("unknown"),
   439  			wantMatch: false,
   440  		},
   441  		{
   442  			name:      "showFrom matches function names",
   443  			profile:   noInlinesProfile,
   444  			showFrom:  regexp.MustCompile("fun1"),
   445  			wantMatch: true,
   446  			wantSampleFuncs: []string{
   447  				"fun0 fun1: 1",
   448  				"fun4 fun5 fun1: 2",
   449  				"fun9 fun4 fun10: 4",
   450  			},
   451  		},
   452  		{
   453  			name:      "showFrom matches file names",
   454  			profile:   noInlinesProfile,
   455  			showFrom:  regexp.MustCompile("file1"),
   456  			wantMatch: true,
   457  			wantSampleFuncs: []string{
   458  				"fun0 fun1: 1",
   459  				"fun4 fun5 fun1: 2",
   460  				"fun9 fun4 fun10: 4",
   461  			},
   462  		},
   463  		{
   464  			name:      "showFrom matches mapping names",
   465  			profile:   noInlinesProfile,
   466  			showFrom:  regexp.MustCompile("map1"),
   467  			wantMatch: true,
   468  			wantSampleFuncs: []string{
   469  				"fun9 fun4 fun10: 4",
   470  			},
   471  		},
   472  		{
   473  			name:      "showFrom drops frames above highest of multiple matches",
   474  			profile:   noInlinesProfile,
   475  			showFrom:  regexp.MustCompile("fun[12]"),
   476  			wantMatch: true,
   477  			wantSampleFuncs: []string{
   478  				"fun0 fun1 fun2: 1",
   479  				"fun4 fun5 fun1: 2",
   480  				"fun9 fun4 fun10: 4",
   481  			},
   482  		},
   483  		{
   484  			name:      "showFrom matches inline functions",
   485  			profile:   inlinesProfile,
   486  			showFrom:  regexp.MustCompile("fun0|fun5"),
   487  			wantMatch: true,
   488  			wantSampleFuncs: []string{
   489  				"fun0: 1",
   490  				"fun4 fun5: 2",
   491  			},
   492  		},
   493  		{
   494  			name:      "showFrom drops frames above highest of multiple inline matches",
   495  			profile:   inlinesProfile,
   496  			showFrom:  regexp.MustCompile("fun[1245]"),
   497  			wantMatch: true,
   498  			wantSampleFuncs: []string{
   499  				"fun0 fun1 fun2: 1",
   500  				"fun4 fun5: 2",
   501  			},
   502  		},
   503  		{
   504  			name:      "showFrom keeps all lines when matching mapping and function",
   505  			profile:   inlinesProfile,
   506  			showFrom:  regexp.MustCompile("map0|fun5"),
   507  			wantMatch: true,
   508  			wantSampleFuncs: []string{
   509  				"fun0 fun1 fun2 fun3: 1",
   510  				"fun4 fun5 fun6: 2",
   511  			},
   512  		},
   513  		{
   514  			name:      "showFrom matches location with empty lines",
   515  			profile:   emptyLinesProfile,
   516  			showFrom:  regexp.MustCompile("map1"),
   517  			wantMatch: true,
   518  			wantSampleFuncs: []string{
   519  				": 2",
   520  			},
   521  		},
   522  	} {
   523  		t.Run(tc.name, func(t *testing.T) {
   524  			p := tc.profile.Copy()
   525  
   526  			if gotMatch := p.ShowFrom(tc.showFrom); gotMatch != tc.wantMatch {
   527  				t.Errorf("match got %+v, want %+v", gotMatch, tc.wantMatch)
   528  			}
   529  
   530  			if got, want := strings.Join(sampleFuncs(p), "\n")+"\n", strings.Join(tc.wantSampleFuncs, "\n")+"\n"; got != want {
   531  				diff, err := proftest.Diff([]byte(want), []byte(got))
   532  				if err != nil {
   533  					t.Fatalf("failed to get diff: %v", err)
   534  				}
   535  				t.Errorf("profile samples got diff(want->got):\n%s", diff)
   536  			}
   537  		})
   538  	}
   539  }
   540  
   541  // sampleFuncs returns a slice of strings where each string represents one
   542  // profile sample in the format "<fun1> <fun2> <fun3>: <value>". This allows
   543  // the expected values for test cases to be specified in human-readable
   544  // strings.
   545  func sampleFuncs(p *Profile) []string {
   546  	var ret []string
   547  	for _, s := range p.Sample {
   548  		var funcs []string
   549  		for _, loc := range s.Location {
   550  			for _, line := range loc.Line {
   551  				funcs = append(funcs, line.Function.Name)
   552  			}
   553  		}
   554  		ret = append(ret, fmt.Sprintf("%s: %d", strings.Join(funcs, " "), s.Value[0]))
   555  	}
   556  	return ret
   557  }
   558  
   559  func TestTagFilter(t *testing.T) {
   560  	// Perform several forms of tag filtering on the test profile.
   561  
   562  	type filterTestcase struct {
   563  		include, exclude *regexp.Regexp
   564  		im, em           bool
   565  		count            int
   566  	}
   567  
   568  	countTags := func(p *Profile) map[string]bool {
   569  		tags := make(map[string]bool)
   570  
   571  		for _, s := range p.Sample {
   572  			for l := range s.Label {
   573  				tags[l] = true
   574  			}
   575  			for l := range s.NumLabel {
   576  				tags[l] = true
   577  			}
   578  		}
   579  		return tags
   580  	}
   581  
   582  	for tx, tc := range []filterTestcase{
   583  		{nil, nil, true, false, 3},
   584  		{regexp.MustCompile("notfound"), nil, false, false, 0},
   585  		{regexp.MustCompile("key1"), nil, true, false, 1},
   586  		{nil, regexp.MustCompile("key[12]"), true, true, 1},
   587  	} {
   588  		prof := testProfile1.Copy()
   589  		gim, gem := prof.FilterTagsByName(tc.include, tc.exclude)
   590  		if gim != tc.im {
   591  			t.Errorf("Filter #%d, got include match=%v, want %v", tx, gim, tc.im)
   592  		}
   593  		if gem != tc.em {
   594  			t.Errorf("Filter #%d, got exclude match=%v, want %v", tx, gem, tc.em)
   595  		}
   596  		if tags := countTags(prof); len(tags) != tc.count {
   597  			t.Errorf("Filter #%d, got %d tags[%v], want %d", tx, len(tags), tags, tc.count)
   598  		}
   599  	}
   600  }
   601  

View as plain text