blob: d322a91c5a529bbddf9798786e23d6575ad8e22c [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package parser
import (
"fmt"
"mojom/mojom_tool/mojom"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
)
// FakeFileProvider implements FileProvider.
type FakeFileProvider struct {
// This field records the names of the files whose contents were requested.
requestedFileNames []string
}
// FakeFileProvider implements provideContents by recording the name of the file
// whose contents are being requested and then returning the empty string.
func (f *FakeFileProvider) provideContents(fileRef *FileReference) (contents string, fileReadError error) {
f.requestedFileNames = append(f.requestedFileNames, fileRef.specifiedPath)
return "", nil
}
// FakeFileProvider implements findFile by always succeeding to find the
// file and reporting that the |absolutePath| is equal to the |specifiedPath|
// with the prefix "/a/b/c/"
func (f *FakeFileProvider) findFile(fileRef *FileReference) error {
fileRef.absolutePath = fmt.Sprintf("/a/b/c/%s", fileRef.specifiedPath)
return nil
}
type NoOpParseInvoker int
func (NoOpParseInvoker) invokeParse(parser *Parser) {}
// FakeFileExtractor implements FileExtractor
type FakeFileExtractor struct {
// This field maps a file name to the set of import names that we want to
// simulate the given file is importing.
importNames map[string][]string
}
func makeFakeFileExtractor() FakeFileExtractor {
extractor := FakeFileExtractor{}
extractor.importNames = make(map[string][]string)
return extractor
}
// Invoke this method to specify that FakeFileExtractor should simulate the fact
// that the file named |fileName| imports the files specified by |imports|.
func (f *FakeFileExtractor) appendImportsToFile(fileName string, imports ...string) {
if imports != nil {
f.importNames[fileName] = append(f.importNames[fileName], imports...)
}
}
// FakeFileExtractor implements extractMojomFile() by returning an instance
// of MojomFile that has had imports added to it according to the values
// in the map |importNames|.
func (f *FakeFileExtractor) extractMojomFile(parser *Parser) *mojom.MojomFile {
file := parser.GetMojomFile()
for _, importName := range f.importNames[file.CanonicalFileName] {
file.AddImport(mojom.NewImportedFile(importName, nil))
}
return file
}
// TestExpectedFilesParsed tests the logic in function ParseDriver.ParseFiles()
// for determining which files should be parsed.
func TestExpectedFilesParsed(t *testing.T) {
// Construct our fake objects.
fakeFileProvider := FakeFileProvider{}
fakeFileExtractor := makeFakeFileExtractor()
// Our fake file1 will import file3, file4, file5
fakeFileExtractor.appendImportsToFile("/a/b/c/file1", "file3", "file4", "file5")
// Our fake file5 will import file1 and file6
fakeFileExtractor.appendImportsToFile("/a/b/c/file5", "file1", "file6")
// Construct the driver under test
driver := newDriver([]string{}, false, false, &fakeFileProvider, &fakeFileExtractor, NoOpParseInvoker(0))
// Invoke ParseFiles with file1, file2 and file3 as top-level files.
descriptor, err := driver.ParseFiles([]string{"file1", "file2", "file3"})
if err != nil {
t.Errorf(err.Error())
}
// Check that the correct files had their contents requested in the expected order.
expectedFileRefs := []string{"file1", "file2", "file3", "file4", "file5", "file6"}
if !reflect.DeepEqual(expectedFileRefs, fakeFileProvider.requestedFileNames) {
t.Errorf("%v != %v", expectedFileRefs, fakeFileProvider.requestedFileNames)
}
// Retrieve the MojomFile for file1.
file1, ok := descriptor.MojomFilesByName["/a/b/c/file1"]
if !ok {
t.Fatalf("file1 not found.")
}
// Check that it has 3 imports: file3, file4, file5
if file1.Imports == nil {
t.Fatalf("file1 has no imports.")
}
if len(file1.Imports) != 3 {
t.Fatalf("len(file1.Imports)=%d", len(file1.Imports))
}
if file1.Imports[0].CanonicalFileName != "/a/b/c/file3" {
t.Errorf("file1.Imports[0].CanonicalFileName=%q", file1.Imports[0].CanonicalFileName)
}
if file1.Imports[1].CanonicalFileName != "/a/b/c/file4" {
t.Errorf("file1.Imports[1].CanonicalFileName=%q", file1.Imports[1].CanonicalFileName)
}
if file1.Imports[2].CanonicalFileName != "/a/b/c/file5" {
t.Errorf("file1.Imports[1].CanonicalFileName=%q", file1.Imports[2].CanonicalFileName)
}
// Retrieve the MojomFile for file3
file3, ok := descriptor.MojomFilesByName["/a/b/c/file3"]
if !ok {
t.Fatalf("file3 not found.")
}
// We expect the |ImportedFrom| field to be nil and the |SpecifiedFileName|
// to be non-empty because, even though file3
// was imported from file1, it was also a top-level file.
if file3.ImportedFrom != nil {
t.Errorf("file3.ImportedFrom == %v", file3.ImportedFrom)
}
if file3.SpecifiedFileName != "file3" {
t.Errorf("file3.SpecifiedFileName == %v", file3.SpecifiedFileName)
}
// Retrieve the MojomFile for file4
file4, ok := descriptor.MojomFilesByName["/a/b/c/file4"]
if !ok {
t.Fatalf("file4 not found.")
}
// We expect the |ImportedFrom| field to refer to file1 because file4 was imported
// from file1 and was not a top-level file. We expect |SpecifiedFileName| to
// be empty.
if file4.ImportedFrom != file1 {
t.Errorf("file4.ImportedFrom = %v", file4.ImportedFrom)
}
if file4.SpecifiedFileName != "" {
t.Errorf("file4.SpecifiedFileName = %v", file4.SpecifiedFileName)
}
}
// TestMetaDataOnlyMode tests the logic in function ParseDriver.ParseFiles()
// when metaDataOnlyMode is true. Imported files should not be parsed
// and CanonicalFileNames should not be set.
func TestMetaDataOnlyMode(t *testing.T) {
// Construct our fake objects.
fakeFileProvider := FakeFileProvider{}
fakeFileExtractor := makeFakeFileExtractor()
// Our fake file1 will import file3, file4, file5
fakeFileExtractor.appendImportsToFile("/a/b/c/file1", "file3", "file4", "file5")
// Our fake file5 will import file1 and file6
fakeFileExtractor.appendImportsToFile("/a/b/c/file5", "file1", "file6")
// Construct the driver under test
driver := newDriver([]string{}, false, true, &fakeFileProvider, &fakeFileExtractor, NoOpParseInvoker(0))
// Invoke ParseFiles with file1, file2 and file3 as top-level files.
descriptor, err := driver.ParseFiles([]string{"file1", "file2", "file3"})
if err != nil {
t.Errorf(err.Error())
}
// Check that the correct files had their contents requested in the expected order.
expectedFileRefs := []string{"file1", "file2", "file3"}
if !reflect.DeepEqual(expectedFileRefs, fakeFileProvider.requestedFileNames) {
t.Errorf("%v != %v", expectedFileRefs, fakeFileProvider.requestedFileNames)
}
// Retrieve the MojomFile for file1.
file1, ok := descriptor.MojomFilesByName["/a/b/c/file1"]
if !ok {
t.Fatalf("file1 not found.")
}
// Check that it has 3 imports: file3, file4, file5
if file1.Imports == nil {
t.Fatalf("file1 has no imports.")
}
if len(file1.Imports) != 3 {
t.Fatalf("len(file1.Imports)=%d", len(file1.Imports))
}
if file1.Imports[0].CanonicalFileName != "" {
t.Errorf("file1.Imports[0].CanonicalFileName=%q", file1.Imports[0].CanonicalFileName)
}
if file1.Imports[1].CanonicalFileName != "" {
t.Errorf("file1.Imports[1].CanonicalFileName=%q", file1.Imports[1].CanonicalFileName)
}
if file1.Imports[2].CanonicalFileName != "" {
t.Errorf("file1.Imports[1].CanonicalFileName=%q", file1.Imports[2].CanonicalFileName)
}
// Retrieve the MojomFile for file3
file3, ok := descriptor.MojomFilesByName["/a/b/c/file3"]
if !ok {
t.Fatalf("file3 not found.")
}
// We expect the |ImportedFrom| field to be nil and the |SpecifiedFileName|
// to be non-empty because, even though file3
// was imported from file1, it was also a top-level file.
if file3.ImportedFrom != nil {
t.Errorf("file3.ImportedFrom == %v", file3.ImportedFrom)
}
if file3.SpecifiedFileName != "file3" {
t.Errorf("file3.SpecifiedFileName == %v", file3.SpecifiedFileName)
}
// Try to retrieve the MojomFile for file4. It should not be there.
if _, ok = descriptor.MojomFilesByName["/a/b/c/file4"]; ok {
t.Fatalf("file4 was unexpectedly found.")
}
}
// TestOSFileProvider tests the function OSFileProvider.findFiles() and OSFileProvider.provideContents()
func TestOSFileProvider(t *testing.T) {
// Top level files (not imported from anything) are found relative to the current directory.
fileReferenceA := doOSFileProviderTest(t, "../test_data/a/testfile1", nil, nil, "mojom_tool/test_data/a")
fileReferenceB := doOSFileProviderTest(t, "../test_data/b/testfile1", nil, nil, "mojom_tool/test_data/b")
// A file imported from a file in directory 'a' should be found in directory 'a'
doOSFileProviderTest(t, "testfile1", &fileReferenceA, nil, "mojom_tool/test_data/a")
// A file imported from a file in directory 'b' should be found in directory 'a'
doOSFileProviderTest(t, "testfile1", &fileReferenceB, nil, "mojom_tool/test_data/b")
// A file imported from a file in directory 'a' should be found in directory 'b' if there is no file with the
// specified name in directory 'a' and directory 'b' is in the search path.
doOSFileProviderTest(t, "testfile2", &fileReferenceA, []string{"../test_data/b"}, "mojom_tool/test_data/b")
// The file is imported from directory 'a' and directory 'b' is on the search path but there is no file with the
// specified name in either directory and there is a file with the specified name in the current directory.
// The file should be found in the current directory. Note that the last argument is a random string of digits that
// we expect to find in the contents of the file parser_driver_test.go (i.e this file).
doOSFileProviderTest(t, "parse_driver_test.go", &fileReferenceA, []string{"../test_data/b"}, "840274941330987490326243")
}
// doOSFileProviderTest is the workhorse for TestOSFileProvider.
func doOSFileProviderTest(t *testing.T, specifiedPath string, importedFrom *FileReference,
globalImports []string, expectedContents string) FileReference {
fileProvider := new(OSFileProvider)
fileProvider.importDirs = globalImports
fileReference := FileReference{specifiedPath: specifiedPath}
fileReference.importedFrom = importedFrom
// Invoke findFile()
if err := fileProvider.findFile(&fileReference); err != nil {
t.Fatalf(err.Error())
}
// Check that the absolutePath has been populated to the absolute path of a file.
if fileReference.absolutePath == "" {
t.Fatalf("absolutePath is not set for %s", fileReference.specifiedPath)
}
if !filepath.IsAbs(fileReference.absolutePath) {
t.Fatalf("absolutePath is not absolute for %s: %s", fileReference.specifiedPath, fileReference.absolutePath)
}
info, err := os.Stat(fileReference.absolutePath)
if err != nil {
t.Fatalf("cannot stat absolutePath %s: %s", fileReference.absolutePath, err.Error())
}
if info.IsDir() {
t.Fatalf("absolutePath refers to a directory: %s", fileReference.absolutePath)
}
// Check that the dirPath has been populated to the absolute path of a directory.
if fileReference.directoryPath == "" {
t.Fatalf("directoryPath is not set for %s", fileReference.specifiedPath)
}
if !filepath.IsAbs(fileReference.directoryPath) {
t.Fatalf("directoryPath is not absolute for %s: %s", fileReference.specifiedPath, fileReference.directoryPath)
}
info, err = os.Stat(fileReference.directoryPath)
if err != nil {
t.Fatalf("cannot stat directoryPath %s: %s", fileReference.directoryPath, err.Error())
}
if !info.IsDir() {
t.Fatalf("directoryPath does not refer to a directory: %s", fileReference.directoryPath)
}
if filepath.Dir(fileReference.absolutePath) != fileReference.directoryPath {
t.Fatalf("wrong directoryPath expected parent of %s got %s", fileReference.absolutePath, fileReference.directoryPath)
}
contents, err := fileProvider.provideContents(&fileReference)
if err != nil {
t.Errorf("Error from provideContents for %v: %s", fileReference, err.Error())
}
if !strings.Contains(contents, expectedContents) {
t.Errorf("Wrong file contents for %v. Expecting %s got %s.", fileReference, expectedContents, contents)
}
return fileReference
}