package driver import ( "net/url" "os" "path/filepath" "reflect" "testing" ) // settingsDirAndFile returns a directory in which settings should be stored // and the name of the settings file. The caller must delete the directory when // done. func settingsDirAndFile(t *testing.T) (string, string) { tmpDir, err := os.MkdirTemp("", "pprof_settings_test") if err != nil { t.Fatalf("error creating temporary directory: %v", err) } return tmpDir, filepath.Join(tmpDir, "settings.json") } func TestSettings(t *testing.T) { tmpDir, fname := settingsDirAndFile(t) defer os.RemoveAll(tmpDir) s, err := readSettings(fname) if err != nil { t.Fatalf("error reading empty settings: %v", err) } if len(s.Configs) != 0 { t.Fatalf("expected empty settings; got %v", s) } s.Configs = append(s.Configs, namedConfig{ Name: "Foo", config: config{ Focus: "focus", // Ensure that transient fields are not saved/restored. Output: "output", SourcePath: "source", TrimPath: "trim", DivideBy: -2, }, }) if err := writeSettings(fname, s); err != nil { t.Fatal(err) } s2, err := readSettings(fname) if err != nil { t.Fatal(err) } // Change the transient fields to their expected values. s.Configs[0].resetTransient() if !reflect.DeepEqual(s, s2) { t.Fatalf("ReadSettings = %v; expected %v", s2, s) } } func TestParseConfig(t *testing.T) { // Use all the fields to check they are saved/restored from URL. cfg := config{ Output: "", DropNegative: true, CallTree: true, RelativePercentages: true, Unit: "auto", CompactLabels: true, SourcePath: "", TrimPath: "", NodeCount: 10, NodeFraction: 0.1, EdgeFraction: 0.2, Trim: true, Focus: "focus", Ignore: "ignore", PruneFrom: "prune_from", Hide: "hide", Show: "show", ShowFrom: "show_from", TagFocus: "tagfocus", TagIgnore: "tagignore", TagShow: "tagshow", TagHide: "taghide", DivideBy: 1, Mean: true, Normalize: true, Sort: "cum", Granularity: "functions", NoInlines: true, ShowColumns: true, } url, changed := cfg.makeURL(url.URL{}) if !changed { t.Error("applyConfig returned changed=false after applying non-empty config") } cfg2 := defaultConfig() if err := cfg2.applyURL(url.Query()); err != nil { t.Fatalf("fromURL failed: %v", err) } if !reflect.DeepEqual(cfg, cfg2) { t.Fatalf("parsed config = %+v; expected match with %+v", cfg2, cfg) } if url2, changed := cfg.makeURL(url); changed { t.Errorf("ApplyConfig returned changed=true after applying same config (%q instead of expected %q", url2.String(), url.String()) } } // TestDefaultConfig verifies that default config values are omitted from URL. func TestDefaultConfig(t *testing.T) { cfg := defaultConfig() url, changed := cfg.makeURL(url.URL{}) if changed { t.Error("applyConfig returned changed=true after applying default config") } if url.String() != "" { t.Errorf("applyConfig returned %q; expecting %q", url.String(), "") } } func TestConfigMenu(t *testing.T) { // Save some test settings. tmpDir, fname := settingsDirAndFile(t) defer os.RemoveAll(tmpDir) a, b := defaultConfig(), defaultConfig() a.Focus, b.Focus = "foo", "bar" s := &settings{ Configs: []namedConfig{ {Name: "A", config: a}, {Name: "B", config: b}, }, } if err := writeSettings(fname, s); err != nil { t.Fatal("error writing settings", err) } pageURL, _ := url.Parse("/top?f=foo") menu := configMenu(fname, *pageURL) want := []configMenuEntry{ {Name: "Default", URL: "?", Current: false, UserConfig: false}, {Name: "A", URL: "?f=foo", Current: true, UserConfig: true}, {Name: "B", URL: "?f=bar", Current: false, UserConfig: true}, } if !reflect.DeepEqual(menu, want) { t.Errorf("ConfigMenu returned %v; want %v", menu, want) } } func TestEditConfig(t *testing.T) { tmpDir, fname := settingsDirAndFile(t) defer os.RemoveAll(tmpDir) type testConfig struct { name string focus string hide string } type testCase struct { remove bool request string expect []testConfig } for _, c := range []testCase{ // Create setting c1 {false, "/?config=c1&f=foo", []testConfig{ {"c1", "foo", ""}, }}, // Create setting c2 {false, "/?config=c2&h=bar", []testConfig{ {"c1", "foo", ""}, {"c2", "", "bar"}, }}, // Overwrite c1 {false, "/?config=c1&f=baz", []testConfig{ {"c1", "baz", ""}, {"c2", "", "bar"}, }}, // Delete c2 {true, "c2", []testConfig{ {"c1", "baz", ""}, }}, } { if c.remove { if err := removeConfig(fname, c.request); err != nil { t.Errorf("error removing config %s: %v", c.request, err) continue } } else { req, err := url.Parse(c.request) if err != nil { t.Errorf("error parsing request %q: %v", c.request, err) continue } if err := setConfig(fname, *req); err != nil { t.Errorf("error saving request %q: %v", c.request, err) continue } } // Check resulting settings. s, err := readSettings(fname) if err != nil { t.Errorf("error reading settings after applying %q: %v", c.request, err) continue } // Convert to a list that can be compared to c.expect got := make([]testConfig, len(s.Configs)) for i, c := range s.Configs { got[i] = testConfig{c.Name, c.Focus, c.Hide} } if !reflect.DeepEqual(got, c.expect) { t.Errorf("Settings after applying %q = %v; want %v", c.request, got, c.expect) } } } func TestAssign(t *testing.T) { baseConfig := currentConfig() defer setCurrentConfig(baseConfig) // Test assigning to a simple field. if err := configure("nodecount", "20"); err != nil { t.Errorf("error setting nodecount: %v", err) } if n := currentConfig().NodeCount; n != 20 { t.Errorf("incorrect nodecount; expecting 20, got %d", n) } // Test assignment to a group field. if err := configure("granularity", "files"); err != nil { t.Errorf("error setting granularity: %v", err) } if g := currentConfig().Granularity; g != "files" { t.Errorf("incorrect granularity; expecting %v, got %v", "files", g) } // Test assignment to one choice of a group field. if err := configure("lines", "t"); err != nil { t.Errorf("error setting lines: %v", err) } if g := currentConfig().Granularity; g != "lines" { t.Errorf("incorrect granularity; expecting %v, got %v", "lines", g) } // Test assignment to invalid choice, if err := configure("granularity", "cheese"); err == nil { t.Errorf("allowed assignment of invalid granularity") } }