func TestParseInputAndroid(t *testing.T) { // These inputs are valid goodInputs := []struct { input string version string }{ {"W/google-breakpad(0): 1.2.3.4\n", "1.2.3.4"}, {"W/google-breakpad(0): 1234\n", "1234"}, {"W/google-breakpad(0123): 0\n", "0"}, {"W/google-breakpad(0): 0\n #00 pc 006fbe5a libchromeview.so\n", "0"}, {"W/google-breakpad(0): 0\n #00 pc 006fbe5a libchromeview.so (func)\n", "0"}, {"W/google-breakpad(0): 0\n #00 xx 006fbe5a libchromeview.so\n", "0"}, {"W/google-breakpad(0): 0\n #99 pc 006fbe5a libchromeview.so\n", "0"}, } var testmod testModuleInfoServiceAndroid for _, test := range goodInputs { parser := NewAndroidParser(context.Background(), &testmod, "") if err := parser.ParseInput(test.input); err != nil { t.Error("Did not expect error for input: " + test.input) } if test.version != testmod.version { t.Error("Expected version: " + test.version + " for input: " + test.input) } } // These inputs are not valid badInputs := []struct { input string errorStr string }{ {"W/google-breakpad(0): b7247ee2-5177-40fd-8959-33bc2f793db9\n", "Version number of Chrome"}, {"W/google-breakpad(0): 1.2.3.4.\n", "Version number of Chrome"}, {"W/google-breakpad(0): 1234\n #18446744073709551616 pc 006fbe5a /system/lib/libchromeview.so\n", "frame number"}, } for _, test := range badInputs { parser := NewAndroidParser(context.Background(), &testmod, "") if err := parser.ParseInput(test.input); err == nil { t.Error("Expected error for input: " + test.input) } else { if !strings.Contains(err.Error(), test.errorStr) { t.Error("Expected \"" + test.errorStr + "\" as the error") } } } }
// TestSymbolizeAndroid tests the symbolize function of androidParser. This function // is almost identical to the TestSymbolize function in input_apple_test.go. func TestSymbolizeAndroid(t *testing.T) { files := []string{ "android1.txt", "android2.txt", } for _, file := range files { var testmod testModuleInfoServiceAndroid inputData, err := testutils.ReadSourceFile(testdata(file)) if err != nil { t.Errorf("Failed to read file : " + file) continue } tables := []breakpad.SymbolTable{ &testTable{name: "libchromeview.so", symbol: "Framework"}, } parser := NewAndroidParser(context.Background(), &testmod, "") err = parser.ParseInput(string(inputData)) if err != nil { t.Errorf("%s: %s", file, err) continue } // Write the output to a .actual file, which can be used to create a new baseline // .expected file by copying it into the testdata/ directory. actual := parser.Symbolize(tables) actualFileName, actualFile, err := testutils.CreateTempFile(file + ".actual") if err != nil { t.Errorf("Could not create actual file output: %v", err) continue } fmt.Fprint(actualFile, actual) actualFile.Close() expectedFileName := testutils.GetSourceFilePath(testdata(file + ".expected")) err = testutils.CheckFilesEqual(expectedFileName, actualFileName) if err != nil { t.Errorf("Input data for %s does not symbolize to expected output", file) t.Error(err) } } }
func TestGetTableCache(t *testing.T) { *cacheSize = 5 // Create a new Handler. The mux is a throw-away. handler := RegisterHandlers(http.NewServeMux()) supplier := new(cacheTestSupplier) supplier.reset() handler.Init(supplier) const kInitialName = "initial fill #%d" // Supply five tables to max out the cache. go func() { for i := 1; i <= *cacheSize; i++ { supplier.c <- breakpad.SupplierResponse{ Table: newTestTable(fmt.Sprintf(kInitialName, i)), } } supplier.c <- breakpad.SupplierResponse{ Error: errors.New("cache miss when should be cache hit"), } close(supplier.c) }() // Now receieve those five from the cache, twice. for iter := 0; iter < 2; iter++ { for i := 1; i <= *cacheSize; i++ { ident := fmt.Sprintf(kInitialName, i) table, err := handler.getTable(context.Background(), breakpad.SupplierRequest{"module", ident}) if err != nil { t.Errorf("Error getting '%s': %v", ident, err) continue } if table.Identifier() != ident { t.Errorf("Identifier mismatch, expected '%s', got '%s'", ident, table.Identifier()) } } } // Receive pending messages until the channel gets closed. for _ = range supplier.c { } supplier.reset() // After iterating through the cache twice, initial #5 is MRU, so #1 will // be the first to be evicted. const kEvictFirst = "evict initial fill #1" go func() { supplier.c <- breakpad.SupplierResponse{ Table: newTestTable(kEvictFirst), } supplier.c <- breakpad.SupplierResponse{ Error: errors.New("unexpected supplier request"), } close(supplier.c) }() // Get a different table, which will evict #1. table, err := handler.getTable(context.Background(), breakpad.SupplierRequest{"module", kEvictFirst}) if err != nil { t.Errorf("error getting '%s': %v", kEvictFirst, err) } else { if table.Identifier() != kEvictFirst { t.Errorf("Identifier mismatch, expected '%s', got '%s'", kEvictFirst, table.Identifier()) } } // Now get a table that should be in the cache. ident := fmt.Sprintf(kInitialName, 3) table, err = handler.getTable(context.Background(), breakpad.SupplierRequest{"module", ident}) if err != nil { t.Errorf("error getting '%s' after evicting #1: %v", ident, err) } else { if table.Identifier() != ident { t.Errorf("Identifier mismatch, expected '%s', got '%s'", ident, table.Identifier()) } } cacheOrder := []string{ fmt.Sprintf(kInitialName, 2), fmt.Sprintf(kInitialName, 4), fmt.Sprintf(kInitialName, 5), kEvictFirst, fmt.Sprintf(kInitialName, 3), } i := 0 for e := handler.mru.Front(); e != nil; e = e.Next() { ident = cacheOrder[i] if e.Value.(breakpad.SymbolTable).Identifier() != ident { t.Errorf("cache index %d mismatch, expected '%s', got '%v'", i, ident, e.Value) } if _, ok := handler.symbolCache[ident]; !ok { t.Errorf("cache entry '%s' not present in symbol cache", ident) } i++ } if len(handler.symbolCache) != *cacheSize { t.Errorf("symbol cache size mismatch, expected %d, got %d", *cacheSize, len(handler.symbolCache)) } }