github.com/xhghs/rclone@v1.51.1-0.20200430155106-e186a28cced8/fs/filter/filter_test.go (about)

     1  package filter
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"strings"
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/rclone/rclone/fs"
    14  	"github.com/rclone/rclone/fstest/mockobject"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  func TestNewFilterDefault(t *testing.T) {
    20  	f, err := NewFilter(nil)
    21  	require.NoError(t, err)
    22  	assert.False(t, f.Opt.DeleteExcluded)
    23  	assert.Equal(t, fs.SizeSuffix(-1), f.Opt.MinSize)
    24  	assert.Equal(t, fs.SizeSuffix(-1), f.Opt.MaxSize)
    25  	assert.Len(t, f.fileRules.rules, 0)
    26  	assert.Len(t, f.dirRules.rules, 0)
    27  	assert.Nil(t, f.files)
    28  	assert.True(t, f.InActive())
    29  }
    30  
    31  // testFile creates a temp file with the contents
    32  func testFile(t *testing.T, contents string) string {
    33  	out, err := ioutil.TempFile("", "filter_test")
    34  	require.NoError(t, err)
    35  	defer func() {
    36  		err := out.Close()
    37  		require.NoError(t, err)
    38  	}()
    39  	_, err = out.Write([]byte(contents))
    40  	require.NoError(t, err)
    41  	s := out.Name()
    42  	return s
    43  }
    44  
    45  func TestNewFilterForbiddenMixOfFilesFromAndFilterRule(t *testing.T) {
    46  	Opt := DefaultOpt
    47  
    48  	// Set up the input
    49  	Opt.FilterRule = []string{"- filter1", "- filter1b"}
    50  	Opt.FilesFrom = []string{testFile(t, "#comment\nfiles1\nfiles2\n")}
    51  
    52  	rm := func(p string) {
    53  		err := os.Remove(p)
    54  		if err != nil {
    55  			t.Logf("error removing %q: %v", p, err)
    56  		}
    57  	}
    58  	// Reset the input
    59  	defer func() {
    60  		rm(Opt.FilesFrom[0])
    61  	}()
    62  
    63  	_, err := NewFilter(&Opt)
    64  	require.Error(t, err)
    65  	require.Contains(t, err.Error(), "The usage of --files-from overrides all other filters")
    66  }
    67  
    68  func TestNewFilterWithFilesFromAlone(t *testing.T) {
    69  	Opt := DefaultOpt
    70  
    71  	// Set up the input
    72  	Opt.FilesFrom = []string{testFile(t, "#comment\nfiles1\nfiles2\n")}
    73  
    74  	rm := func(p string) {
    75  		err := os.Remove(p)
    76  		if err != nil {
    77  			t.Logf("error removing %q: %v", p, err)
    78  		}
    79  	}
    80  	// Reset the input
    81  	defer func() {
    82  		rm(Opt.FilesFrom[0])
    83  	}()
    84  
    85  	f, err := NewFilter(&Opt)
    86  	require.NoError(t, err)
    87  	assert.Len(t, f.files, 2)
    88  	for _, name := range []string{"files1", "files2"} {
    89  		_, ok := f.files[name]
    90  		if !ok {
    91  			t.Errorf("Didn't find file %q in f.files", name)
    92  		}
    93  	}
    94  }
    95  
    96  func TestNewFilterFullExceptFilesFromOpt(t *testing.T) {
    97  	Opt := DefaultOpt
    98  
    99  	mins := fs.SizeSuffix(100 * 1024)
   100  	maxs := fs.SizeSuffix(1000 * 1024)
   101  
   102  	// Set up the input
   103  	Opt.DeleteExcluded = true
   104  	Opt.FilterRule = []string{"- filter1", "- filter1b"}
   105  	Opt.FilterFrom = []string{testFile(t, "#comment\n+ filter2\n- filter3\n")}
   106  	Opt.ExcludeRule = []string{"exclude1"}
   107  	Opt.ExcludeFrom = []string{testFile(t, "#comment\nexclude2\nexclude3\n")}
   108  	Opt.IncludeRule = []string{"include1"}
   109  	Opt.IncludeFrom = []string{testFile(t, "#comment\ninclude2\ninclude3\n")}
   110  	Opt.MinSize = mins
   111  	Opt.MaxSize = maxs
   112  
   113  	rm := func(p string) {
   114  		err := os.Remove(p)
   115  		if err != nil {
   116  			t.Logf("error removing %q: %v", p, err)
   117  		}
   118  	}
   119  	// Reset the input
   120  	defer func() {
   121  		rm(Opt.FilterFrom[0])
   122  		rm(Opt.ExcludeFrom[0])
   123  		rm(Opt.IncludeFrom[0])
   124  	}()
   125  
   126  	f, err := NewFilter(&Opt)
   127  	require.NoError(t, err)
   128  	assert.True(t, f.Opt.DeleteExcluded)
   129  	assert.Equal(t, f.Opt.MinSize, mins)
   130  	assert.Equal(t, f.Opt.MaxSize, maxs)
   131  	got := f.DumpFilters()
   132  	want := `--- File filter rules ---
   133  + (^|/)include1$
   134  + (^|/)include2$
   135  + (^|/)include3$
   136  - (^|/)exclude1$
   137  - (^|/)exclude2$
   138  - (^|/)exclude3$
   139  - (^|/)filter1$
   140  - (^|/)filter1b$
   141  + (^|/)filter2$
   142  - (^|/)filter3$
   143  - ^.*$
   144  --- Directory filter rules ---
   145  + ^.*$
   146  - ^.*$`
   147  	assert.Equal(t, want, got)
   148  	assert.False(t, f.InActive())
   149  }
   150  
   151  type includeTest struct {
   152  	in      string
   153  	size    int64
   154  	modTime int64
   155  	want    bool
   156  }
   157  
   158  func testInclude(t *testing.T, f *Filter, tests []includeTest) {
   159  	for _, test := range tests {
   160  		got := f.Include(test.in, test.size, time.Unix(test.modTime, 0))
   161  		assert.Equal(t, test.want, got, fmt.Sprintf("in=%q, size=%v, modTime=%v", test.in, test.size, time.Unix(test.modTime, 0)))
   162  	}
   163  }
   164  
   165  type includeDirTest struct {
   166  	in   string
   167  	want bool
   168  }
   169  
   170  func testDirInclude(t *testing.T, f *Filter, tests []includeDirTest) {
   171  	for _, test := range tests {
   172  		got, err := f.IncludeDirectory(context.Background(), nil)(test.in)
   173  		require.NoError(t, err)
   174  		assert.Equal(t, test.want, got, test.in)
   175  	}
   176  }
   177  
   178  func TestNewFilterIncludeFiles(t *testing.T) {
   179  	f, err := NewFilter(nil)
   180  	require.NoError(t, err)
   181  	err = f.AddFile("file1.jpg")
   182  	require.NoError(t, err)
   183  	err = f.AddFile("/file2.jpg")
   184  	require.NoError(t, err)
   185  	assert.Equal(t, FilesMap{
   186  		"file1.jpg": {},
   187  		"file2.jpg": {},
   188  	}, f.files)
   189  	assert.Equal(t, FilesMap{}, f.dirs)
   190  	testInclude(t, f, []includeTest{
   191  		{"file1.jpg", 0, 0, true},
   192  		{"file2.jpg", 1, 0, true},
   193  		{"potato/file2.jpg", 2, 0, false},
   194  		{"file3.jpg", 3, 0, false},
   195  	})
   196  	assert.False(t, f.InActive())
   197  }
   198  
   199  func TestNewFilterIncludeFilesDirs(t *testing.T) {
   200  	f, err := NewFilter(nil)
   201  	require.NoError(t, err)
   202  	for _, path := range []string{
   203  		"path/to/dir/file1.png",
   204  		"/path/to/dir/file2.png",
   205  		"/path/to/file3.png",
   206  		"/path/to/dir2/file4.png",
   207  	} {
   208  		err = f.AddFile(path)
   209  		require.NoError(t, err)
   210  	}
   211  	assert.Equal(t, FilesMap{
   212  		"path":         {},
   213  		"path/to":      {},
   214  		"path/to/dir":  {},
   215  		"path/to/dir2": {},
   216  	}, f.dirs)
   217  	testDirInclude(t, f, []includeDirTest{
   218  		{"path", true},
   219  		{"path/to", true},
   220  		{"path/to/", true},
   221  		{"/path/to", true},
   222  		{"/path/to/", true},
   223  		{"path/to/dir", true},
   224  		{"path/to/dir2", true},
   225  		{"path/too", false},
   226  		{"path/three", false},
   227  		{"four", false},
   228  	})
   229  }
   230  
   231  func TestNewFilterHaveFilesFrom(t *testing.T) {
   232  	f, err := NewFilter(nil)
   233  	require.NoError(t, err)
   234  
   235  	assert.Equal(t, false, f.HaveFilesFrom())
   236  
   237  	require.NoError(t, f.AddFile("file"))
   238  
   239  	assert.Equal(t, true, f.HaveFilesFrom())
   240  }
   241  
   242  func TestNewFilterMakeListR(t *testing.T) {
   243  	f, err := NewFilter(nil)
   244  	require.NoError(t, err)
   245  
   246  	// Check error if no files
   247  	listR := f.MakeListR(context.Background(), nil)
   248  	err = listR(context.Background(), "", nil)
   249  	assert.EqualError(t, err, errFilesFromNotSet.Error())
   250  
   251  	// Add some files
   252  	for _, path := range []string{
   253  		"path/to/dir/file1.png",
   254  		"/path/to/dir/file2.png",
   255  		"/path/to/file3.png",
   256  		"/path/to/dir2/file4.png",
   257  		"notfound",
   258  	} {
   259  		err = f.AddFile(path)
   260  		require.NoError(t, err)
   261  	}
   262  
   263  	assert.Equal(t, 5, len(f.files))
   264  
   265  	// NewObject function for MakeListR
   266  	newObjects := FilesMap{}
   267  	var newObjectMu sync.Mutex
   268  	NewObject := func(ctx context.Context, remote string) (fs.Object, error) {
   269  		newObjectMu.Lock()
   270  		defer newObjectMu.Unlock()
   271  		if remote == "notfound" {
   272  			return nil, fs.ErrorObjectNotFound
   273  		} else if remote == "error" {
   274  			return nil, assert.AnError
   275  		}
   276  		newObjects[remote] = struct{}{}
   277  		return mockobject.New(remote), nil
   278  
   279  	}
   280  
   281  	// Callback for ListRFn
   282  	listRObjects := FilesMap{}
   283  	var callbackMu sync.Mutex
   284  	listRcallback := func(entries fs.DirEntries) error {
   285  		callbackMu.Lock()
   286  		defer callbackMu.Unlock()
   287  		for _, entry := range entries {
   288  			listRObjects[entry.Remote()] = struct{}{}
   289  		}
   290  		return nil
   291  	}
   292  
   293  	// Make the listR and call it
   294  	listR = f.MakeListR(context.Background(), NewObject)
   295  	err = listR(context.Background(), "", listRcallback)
   296  	require.NoError(t, err)
   297  
   298  	// Check that the correct objects were created and listed
   299  	want := FilesMap{
   300  		"path/to/dir/file1.png":  {},
   301  		"path/to/dir/file2.png":  {},
   302  		"path/to/file3.png":      {},
   303  		"path/to/dir2/file4.png": {},
   304  	}
   305  	assert.Equal(t, want, newObjects)
   306  	assert.Equal(t, want, listRObjects)
   307  
   308  	// Now check an error is returned from NewObject
   309  	require.NoError(t, f.AddFile("error"))
   310  	err = listR(context.Background(), "", listRcallback)
   311  	require.EqualError(t, err, assert.AnError.Error())
   312  }
   313  
   314  func TestNewFilterMinSize(t *testing.T) {
   315  	f, err := NewFilter(nil)
   316  	require.NoError(t, err)
   317  	f.Opt.MinSize = 100
   318  	testInclude(t, f, []includeTest{
   319  		{"file1.jpg", 100, 0, true},
   320  		{"file2.jpg", 101, 0, true},
   321  		{"potato/file2.jpg", 99, 0, false},
   322  	})
   323  	assert.False(t, f.InActive())
   324  }
   325  
   326  func TestNewFilterMaxSize(t *testing.T) {
   327  	f, err := NewFilter(nil)
   328  	require.NoError(t, err)
   329  	f.Opt.MaxSize = 100
   330  	testInclude(t, f, []includeTest{
   331  		{"file1.jpg", 100, 0, true},
   332  		{"file2.jpg", 101, 0, false},
   333  		{"potato/file2.jpg", 99, 0, true},
   334  	})
   335  	assert.False(t, f.InActive())
   336  }
   337  
   338  func TestNewFilterMinAndMaxAge(t *testing.T) {
   339  	f, err := NewFilter(nil)
   340  	require.NoError(t, err)
   341  	f.ModTimeFrom = time.Unix(1440000002, 0)
   342  	f.ModTimeTo = time.Unix(1440000003, 0)
   343  	testInclude(t, f, []includeTest{
   344  		{"file1.jpg", 100, 1440000000, false},
   345  		{"file2.jpg", 101, 1440000001, false},
   346  		{"file3.jpg", 102, 1440000002, true},
   347  		{"potato/file1.jpg", 98, 1440000003, true},
   348  		{"potato/file2.jpg", 99, 1440000004, false},
   349  	})
   350  	assert.False(t, f.InActive())
   351  }
   352  
   353  func TestNewFilterMinAge(t *testing.T) {
   354  	f, err := NewFilter(nil)
   355  	require.NoError(t, err)
   356  	f.ModTimeTo = time.Unix(1440000002, 0)
   357  	testInclude(t, f, []includeTest{
   358  		{"file1.jpg", 100, 1440000000, true},
   359  		{"file2.jpg", 101, 1440000001, true},
   360  		{"file3.jpg", 102, 1440000002, true},
   361  		{"potato/file1.jpg", 98, 1440000003, false},
   362  		{"potato/file2.jpg", 99, 1440000004, false},
   363  	})
   364  	assert.False(t, f.InActive())
   365  }
   366  
   367  func TestNewFilterMaxAge(t *testing.T) {
   368  	f, err := NewFilter(nil)
   369  	require.NoError(t, err)
   370  	f.ModTimeFrom = time.Unix(1440000002, 0)
   371  	testInclude(t, f, []includeTest{
   372  		{"file1.jpg", 100, 1440000000, false},
   373  		{"file2.jpg", 101, 1440000001, false},
   374  		{"file3.jpg", 102, 1440000002, true},
   375  		{"potato/file1.jpg", 98, 1440000003, true},
   376  		{"potato/file2.jpg", 99, 1440000004, true},
   377  	})
   378  	assert.False(t, f.InActive())
   379  }
   380  
   381  func TestNewFilterMatches(t *testing.T) {
   382  	f, err := NewFilter(nil)
   383  	require.NoError(t, err)
   384  	add := func(s string) {
   385  		err := f.AddRule(s)
   386  		require.NoError(t, err)
   387  	}
   388  	add("+ cleared")
   389  	add("!")
   390  	add("- /file1.jpg")
   391  	add("+ /file2.png")
   392  	add("+ /*.jpg")
   393  	add("- /*.png")
   394  	add("- /potato")
   395  	add("+ /sausage1")
   396  	add("+ /sausage2*")
   397  	add("+ /sausage3**")
   398  	add("+ /a/*.jpg")
   399  	add("- *")
   400  	testInclude(t, f, []includeTest{
   401  		{"cleared", 100, 0, false},
   402  		{"file1.jpg", 100, 0, false},
   403  		{"file2.png", 100, 0, true},
   404  		{"FILE2.png", 100, 0, false},
   405  		{"afile2.png", 100, 0, false},
   406  		{"file3.jpg", 101, 0, true},
   407  		{"file4.png", 101, 0, false},
   408  		{"potato", 101, 0, false},
   409  		{"sausage1", 101, 0, true},
   410  		{"sausage1/potato", 101, 0, false},
   411  		{"sausage2potato", 101, 0, true},
   412  		{"sausage2/potato", 101, 0, false},
   413  		{"sausage3/potato", 101, 0, true},
   414  		{"a/one.jpg", 101, 0, true},
   415  		{"a/one.png", 101, 0, false},
   416  		{"unicorn", 99, 0, false},
   417  	})
   418  	testDirInclude(t, f, []includeDirTest{
   419  		{"sausage1", false},
   420  		{"sausage2", false},
   421  		{"sausage2/sub", false},
   422  		{"sausage2/sub/dir", false},
   423  		{"sausage3", true},
   424  		{"SAUSAGE3", false},
   425  		{"sausage3/sub", true},
   426  		{"sausage3/sub/dir", true},
   427  		{"sausage4", false},
   428  		{"a", true},
   429  	})
   430  	assert.False(t, f.InActive())
   431  }
   432  
   433  func TestNewFilterMatchesIgnoreCase(t *testing.T) {
   434  	f, err := NewFilter(nil)
   435  	require.NoError(t, err)
   436  	f.Opt.IgnoreCase = true
   437  	add := func(s string) {
   438  		err := f.AddRule(s)
   439  		require.NoError(t, err)
   440  	}
   441  	add("+ /file2.png")
   442  	add("+ /sausage3**")
   443  	add("- *")
   444  	testInclude(t, f, []includeTest{
   445  		{"file2.png", 100, 0, true},
   446  		{"FILE2.png", 100, 0, true},
   447  	})
   448  	testDirInclude(t, f, []includeDirTest{
   449  		{"sausage3", true},
   450  		{"SAUSAGE3", true},
   451  	})
   452  	assert.False(t, f.InActive())
   453  }
   454  
   455  func TestFilterAddDirRuleOrFileRule(t *testing.T) {
   456  	for _, test := range []struct {
   457  		included bool
   458  		glob     string
   459  		want     string
   460  	}{
   461  		{
   462  			false,
   463  			"potato",
   464  			`--- File filter rules ---
   465  - (^|/)potato$
   466  --- Directory filter rules ---`,
   467  		},
   468  		{
   469  			true,
   470  			"potato",
   471  			`--- File filter rules ---
   472  + (^|/)potato$
   473  --- Directory filter rules ---
   474  + ^.*$`,
   475  		},
   476  		{
   477  			false,
   478  			"*",
   479  			`--- File filter rules ---
   480  - (^|/)[^/]*$
   481  --- Directory filter rules ---
   482  - ^.*$`,
   483  		},
   484  		{
   485  			true,
   486  			"*",
   487  			`--- File filter rules ---
   488  + (^|/)[^/]*$
   489  --- Directory filter rules ---
   490  + ^.*$`,
   491  		},
   492  		{
   493  			false,
   494  			".*{,/**}",
   495  			`--- File filter rules ---
   496  - (^|/)\.[^/]*(|/.*)$
   497  --- Directory filter rules ---
   498  - (^|/)\.[^/]*(|/.*)$`,
   499  		},
   500  		{
   501  			true,
   502  			"a/b/c/d",
   503  			`--- File filter rules ---
   504  + (^|/)a/b/c/d$
   505  --- Directory filter rules ---
   506  + (^|/)a/b/c/$
   507  + (^|/)a/b/$
   508  + (^|/)a/$`,
   509  		},
   510  	} {
   511  		f, err := NewFilter(nil)
   512  		require.NoError(t, err)
   513  		err = f.Add(test.included, test.glob)
   514  		require.NoError(t, err)
   515  		got := f.DumpFilters()
   516  		assert.Equal(t, test.want, got, fmt.Sprintf("Add(%v, %q)", test.included, test.glob))
   517  	}
   518  }
   519  
   520  func TestFilterForEachLine(t *testing.T) {
   521  	file := testFile(t, `; comment
   522  one
   523  # another comment
   524  
   525  
   526  two
   527   # indented comment
   528  three  
   529  four    
   530  five
   531    six  `)
   532  	defer func() {
   533  		err := os.Remove(file)
   534  		require.NoError(t, err)
   535  	}()
   536  	lines := []string{}
   537  	err := forEachLine(file, func(s string) error {
   538  		lines = append(lines, s)
   539  		return nil
   540  	})
   541  	require.NoError(t, err)
   542  	assert.Equal(t, "one,two,three,four,five,six", strings.Join(lines, ","))
   543  }
   544  
   545  func TestFilterMatchesFromDocs(t *testing.T) {
   546  	for _, test := range []struct {
   547  		glob       string
   548  		included   bool
   549  		file       string
   550  		ignoreCase bool
   551  	}{
   552  		{"file.jpg", true, "file.jpg", false},
   553  		{"file.jpg", true, "directory/file.jpg", false},
   554  		{"file.jpg", false, "afile.jpg", false},
   555  		{"file.jpg", false, "directory/afile.jpg", false},
   556  		{"/file.jpg", true, "file.jpg", false},
   557  		{"/file.jpg", false, "afile.jpg", false},
   558  		{"/file.jpg", false, "directory/file.jpg", false},
   559  		{"*.jpg", true, "file.jpg", false},
   560  		{"*.jpg", true, "directory/file.jpg", false},
   561  		{"*.jpg", false, "file.jpg/anotherfile.png", false},
   562  		{"dir/**", true, "dir/file.jpg", false},
   563  		{"dir/**", true, "dir/dir1/dir2/file.jpg", false},
   564  		{"dir/**", false, "directory/file.jpg", false},
   565  		{"dir/**", false, "adir/file.jpg", false},
   566  		{"l?ss", true, "less", false},
   567  		{"l?ss", true, "lass", false},
   568  		{"l?ss", false, "floss", false},
   569  		{"h[ae]llo", true, "hello", false},
   570  		{"h[ae]llo", true, "hallo", false},
   571  		{"h[ae]llo", false, "hullo", false},
   572  		{"{one,two}_potato", true, "one_potato", false},
   573  		{"{one,two}_potato", true, "two_potato", false},
   574  		{"{one,two}_potato", false, "three_potato", false},
   575  		{"{one,two}_potato", false, "_potato", false},
   576  		{"\\*.jpg", true, "*.jpg", false},
   577  		{"\\\\.jpg", true, "\\.jpg", false},
   578  		{"\\[one\\].jpg", true, "[one].jpg", false},
   579  		{"potato", true, "potato", false},
   580  		{"potato", false, "POTATO", false},
   581  		{"potato", true, "potato", true},
   582  		{"potato", true, "POTATO", true},
   583  	} {
   584  		f, err := NewFilter(nil)
   585  		require.NoError(t, err)
   586  		if test.ignoreCase {
   587  			f.Opt.IgnoreCase = true
   588  		}
   589  		err = f.Add(true, test.glob)
   590  		require.NoError(t, err)
   591  		err = f.Add(false, "*")
   592  		require.NoError(t, err)
   593  		included := f.Include(test.file, 0, time.Unix(0, 0))
   594  		if included != test.included {
   595  			t.Errorf("%q match %q: want %v got %v", test.glob, test.file, test.included, included)
   596  		}
   597  	}
   598  }
   599  
   600  func TestNewFilterUsesDirectoryFilters(t *testing.T) {
   601  	for i, test := range []struct {
   602  		rules []string
   603  		want  bool
   604  	}{
   605  		{
   606  			rules: []string{},
   607  			want:  false,
   608  		},
   609  		{
   610  			rules: []string{
   611  				"+ *",
   612  			},
   613  			want: false,
   614  		},
   615  		{
   616  			rules: []string{
   617  				"+ *.jpg",
   618  				"- *",
   619  			},
   620  			want: false,
   621  		},
   622  		{
   623  			rules: []string{
   624  				"- *.jpg",
   625  			},
   626  			want: false,
   627  		},
   628  		{
   629  			rules: []string{
   630  				"- *.jpg",
   631  				"+ *",
   632  			},
   633  			want: false,
   634  		},
   635  		{
   636  			rules: []string{
   637  				"+ dir/*.jpg",
   638  				"- *",
   639  			},
   640  			want: true,
   641  		},
   642  		{
   643  			rules: []string{
   644  				"+ dir/**",
   645  			},
   646  			want: true,
   647  		},
   648  		{
   649  			rules: []string{
   650  				"- dir/**",
   651  			},
   652  			want: true,
   653  		},
   654  		{
   655  			rules: []string{
   656  				"- /dir/**",
   657  			},
   658  			want: true,
   659  		},
   660  	} {
   661  		what := fmt.Sprintf("#%d", i)
   662  		f, err := NewFilter(nil)
   663  		require.NoError(t, err)
   664  		for _, rule := range test.rules {
   665  			err := f.AddRule(rule)
   666  			require.NoError(t, err, what)
   667  		}
   668  		got := f.UsesDirectoryFilters()
   669  		assert.Equal(t, test.want, got, fmt.Sprintf("%s: %s", what, f.DumpFilters()))
   670  	}
   671  }