// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/src/command_line/arguments.dart';
import 'package:analyzer/src/context/builder.dart';
import 'package:analyzer/src/context/context_root.dart';
import 'package:analyzer/src/context/source.dart';
import 'package:analyzer/src/file_system/file_system.dart';
import 'package:analyzer/src/generated/bazel.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/lint/linter.dart';
import 'package:analyzer/src/lint/registry.dart';
import 'package:analyzer/src/services/lint.dart';
import 'package:analyzer/src/source/package_map_resolver.dart';
import 'package:args/args.dart';
import 'package:package_config/packages.dart';
import 'package:package_config/src/packages_impl.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';

import '../../embedder_tests.dart';
import '../../generated/test_support.dart';
import 'mock_sdk.dart';

main() {
  defineReflectiveSuite(() {
    defineReflectiveTests(ContextBuilderTest);
    defineReflectiveTests(EmbedderYamlLocatorTest);
  });
}

@reflectiveTest
class ContextBuilderTest extends EngineTestCase {
  /**
   * The resource provider to be used by tests.
   */
  MemoryResourceProvider resourceProvider;

  /**
   * The path context used to manipulate file paths.
   */
  path.Context pathContext;

  /**
   * The SDK manager used by the tests;
   */
  DartSdkManager sdkManager;

  /**
   * The content cache used by the tests.
   */
  ContentCache contentCache;

  /**
   * The options passed to the context builder.
   */
  ContextBuilderOptions builderOptions = new ContextBuilderOptions();

  /**
   * The context builder to be used in the test.
   */
  ContextBuilder builder;

  /**
   * The path to the default SDK, or `null` if the test has not explicitly
   * invoked [createDefaultSdk].
   */
  String defaultSdkPath = null;

  _MockLintRule _mockLintRule;
  _MockLintRule _mockLintRule2;
  _MockLintRule _mockLintRule3;
  _MockLintRule _mockPublicMemberApiDocs;

  Uri convertedDirectoryUri(String directoryPath) {
    return new Uri.directory(resourceProvider.convertPath(directoryPath),
        windows: pathContext.style == path.windows.style);
  }

  void createDefaultSdk(Folder sdkDir) {
    defaultSdkPath = pathContext.join(sdkDir.path, 'default', 'sdk');
    String librariesFilePath = pathContext.join(defaultSdkPath, 'lib',
        '_internal', 'sdk_library_metadata', 'lib', 'libraries.dart');
    resourceProvider.newFile(librariesFilePath, r'''
const Map<String, LibraryInfo> libraries = const {
  "async": const LibraryInfo("async/async.dart"),
  "core": const LibraryInfo("core/core.dart"),
};
''');
    sdkManager = new DartSdkManager(defaultSdkPath, false);
    builder = new ContextBuilder(resourceProvider, sdkManager, contentCache,
        options: builderOptions);
  }

  void createFile(String path, String content) {
    resourceProvider.newFile(path, content);
  }

  @override
  void setUp() {
    resourceProvider = new MemoryResourceProvider();
    pathContext = resourceProvider.pathContext;
    new MockSdk(resourceProvider: resourceProvider);
    sdkManager =
        new DartSdkManager(resourceProvider.convertPath('/sdk'), false);
    contentCache = new ContentCache();
    builder = new ContextBuilder(resourceProvider, sdkManager, contentCache,
        options: builderOptions);
  }

  @failingTest
  void test_buildContext() {
    fail('Incomplete test');
  }

  void test_cmdline_lint_default() {
    _defineMockLintRules();
    ArgParser argParser = new ArgParser();
    defineAnalysisArguments(argParser);
    ArgResults argResults = argParser.parse(['--$lintsFlag']);
    var builder = new ContextBuilder(resourceProvider, sdkManager, contentCache,
        options: createContextBuilderOptions(argResults));

    AnalysisOptionsImpl expected = new AnalysisOptionsImpl();
    expected.lint = true;
    expected.lintRules = Registry.ruleRegistry.defaultRules;

    String path = resourceProvider.convertPath('/some/directory/path');
    String filePath =
        pathContext.join(path, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
    resourceProvider.newFile(filePath, '''
''');

    AnalysisOptions options = builder.getAnalysisOptions(path);
    _expectEqualOptions(options, expected);
  }

  void test_cmdline_lint_defined() {
    _defineMockLintRules();
    ArgParser argParser = new ArgParser();
    defineAnalysisArguments(argParser);
    ArgResults argResults = argParser.parse(['--$lintsFlag']);
    var builder = new ContextBuilder(resourceProvider, sdkManager, contentCache,
        options: createContextBuilderOptions(argResults));

    AnalysisOptionsImpl expected = new AnalysisOptionsImpl();
    expected.lint = true;
    expected.lintRules = <LintRule>[
      Registry.ruleRegistry['mock_lint_rule'],
    ];

    String path = resourceProvider.convertPath('/some/directory/path');
    String filePath =
        pathContext.join(path, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
    resourceProvider.newFile(filePath, '''
linter:
  rules:
    - mock_lint_rule
''');

    AnalysisOptions options = builder.getAnalysisOptions(path);
    _expectEqualOptions(options, expected);
  }

  void test_cmdline_lint_off() {
    ArgParser argParser = new ArgParser();
    defineAnalysisArguments(argParser);
    ArgResults argResults = argParser.parse(['--no-$lintsFlag']);
    var builder = new ContextBuilder(resourceProvider, sdkManager, contentCache,
        options: createContextBuilderOptions(argResults));

    AnalysisOptionsImpl expected = new AnalysisOptionsImpl();
    expected.lint = false;
    expected.lintRules = <LintRule>[
      Registry.ruleRegistry['mock_lint_rule'],
    ];

    String path = resourceProvider.convertPath('/some/directory/path');
    String filePath =
        pathContext.join(path, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
    resourceProvider.newFile(filePath, '''
linter:
  rules:
    - mock_lint_rule
''');

    AnalysisOptions options = builder.getAnalysisOptions(path);
    _expectEqualOptions(options, expected);
  }

  void test_cmdline_lint_unspecified_1() {
    ArgParser argParser = new ArgParser();
    defineAnalysisArguments(argParser);
    ArgResults argResults = argParser.parse([]);
    var builder = new ContextBuilder(resourceProvider, sdkManager, contentCache,
        options: createContextBuilderOptions(argResults));

    AnalysisOptionsImpl expected = new AnalysisOptionsImpl();
    expected.lint = true;
    expected.lintRules = <LintRule>[
      Registry.ruleRegistry['mock_lint_rule'],
    ];

    String path = resourceProvider.convertPath('/some/directory/path');
    String filePath =
        pathContext.join(path, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
    resourceProvider.newFile(filePath, '''
linter:
  rules:
    - mock_lint_rule
''');

    AnalysisOptions options = builder.getAnalysisOptions(path);
    _expectEqualOptions(options, expected);
  }

  void test_cmdline_lint_unspecified_2() {
    ArgParser argParser = new ArgParser();
    defineAnalysisArguments(argParser);
    ArgResults argResults = argParser.parse([]);
    var builder = new ContextBuilder(resourceProvider, sdkManager, contentCache,
        options: createContextBuilderOptions(argResults));

    AnalysisOptionsImpl expected = new AnalysisOptionsImpl();
    expected.lint = false;
    expected.lintRules = <LintRule>[];

    String path = resourceProvider.convertPath('/some/directory/path');
    String filePath =
        pathContext.join(path, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
    resourceProvider.newFile(filePath, '''
''');

    AnalysisOptions options = builder.getAnalysisOptions(path);
    _expectEqualOptions(options, expected);
  }

  void test_cmdline_options_override_options_file() {
    ArgParser argParser = new ArgParser();
    defineAnalysisArguments(argParser);
    ArgResults argResults = argParser.parse(['--$enableSuperMixinFlag']);
    var builder = new ContextBuilder(resourceProvider, sdkManager, contentCache,
        options: createContextBuilderOptions(argResults));

    AnalysisOptionsImpl expected = new AnalysisOptionsImpl();
    expected.enableSuperMixins = true;
    expected.previewDart2 = true;

    String path = resourceProvider.convertPath('/some/directory/path');
    String filePath =
        pathContext.join(path, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
    resourceProvider.newFile(filePath, '''
analyzer:
  language:
    enablePreviewDart2: true
''');

    AnalysisOptions options = builder.getAnalysisOptions(path);
    _expectEqualOptions(options, expected);
  }

  void test_convertPackagesToMap_noPackages() {
    expect(builder.convertPackagesToMap(Packages.noPackages), isEmpty);
  }

  void test_convertPackagesToMap_null() {
    expect(builder.convertPackagesToMap(null), isEmpty);
  }

  void test_convertPackagesToMap_packages() {
    String fooName = 'foo';
    String fooPath = resourceProvider.convertPath('/pkg/foo');
    Uri fooUri = pathContext.toUri(fooPath);
    String barName = 'bar';
    String barPath = resourceProvider.convertPath('/pkg/bar');
    Uri barUri = pathContext.toUri(barPath);

    MapPackages packages = new MapPackages({fooName: fooUri, barName: barUri});
    Map<String, List<Folder>> result = builder.convertPackagesToMap(packages);
    expect(result, isNotNull);
    expect(result, hasLength(2));
    expect(result[fooName], hasLength(1));
    expect(result[fooName][0].path, fooPath);
    expect(result[barName], hasLength(1));
    expect(result[barName][0].path, barPath);
  }

  void test_createDefaultOptions_default() {
    // Invert a subset of the options to ensure that the default options are
    // being returned.
    AnalysisOptionsImpl defaultOptions = new AnalysisOptionsImpl();
    defaultOptions.dart2jsHint = !defaultOptions.dart2jsHint;
    defaultOptions.enableLazyAssignmentOperators =
        !defaultOptions.enableLazyAssignmentOperators;
    defaultOptions.enableSuperMixins = !defaultOptions.enableSuperMixins;
    builderOptions.defaultOptions = defaultOptions;
    AnalysisOptions options = builder.createDefaultOptions();
    _expectEqualOptions(options, defaultOptions);
  }

  void test_createDefaultOptions_noDefault() {
    AnalysisOptions options = builder.createDefaultOptions();
    _expectEqualOptions(options, new AnalysisOptionsImpl());
  }

  void test_createPackageMap_fromPackageDirectory_explicit() {
    // Use a package directory that is outside the project directory.
    String rootPath = resourceProvider.convertPath('/root');
    String projectPath = pathContext.join(rootPath, 'project');
    String packageDirPath = pathContext.join(rootPath, 'packages');
    String fooName = 'foo';
    String fooPath = pathContext.join(packageDirPath, fooName);
    String barName = 'bar';
    String barPath = pathContext.join(packageDirPath, barName);
    resourceProvider.newFolder(projectPath);
    resourceProvider.newFolder(fooPath);
    resourceProvider.newFolder(barPath);

    builderOptions.defaultPackagesDirectoryPath = packageDirPath;

    Packages packages = builder.createPackageMap(projectPath);
    expect(packages, isNotNull);
    Map<String, Uri> map = packages.asMap();
    expect(map, hasLength(2));
    expect(map[fooName], convertedDirectoryUri(fooPath));
    expect(map[barName], convertedDirectoryUri(barPath));
  }

  void test_createPackageMap_fromPackageDirectory_inRoot() {
    // Use a package directory that is inside the project directory.
    String projectPath = resourceProvider.convertPath('/root/project');
    String packageDirPath = pathContext.join(projectPath, 'packages');
    String fooName = 'foo';
    String fooPath = pathContext.join(packageDirPath, fooName);
    String barName = 'bar';
    String barPath = pathContext.join(packageDirPath, barName);
    resourceProvider.newFolder(fooPath);
    resourceProvider.newFolder(barPath);

    Packages packages = builder.createPackageMap(projectPath);
    expect(packages, isNotNull);
    Map<String, Uri> map = packages.asMap();
    expect(map, hasLength(2));
    expect(map[fooName], convertedDirectoryUri(fooPath));
    expect(map[barName], convertedDirectoryUri(barPath));
  }

  void test_createPackageMap_fromPackageFile_explicit() {
    // Use a package file that is outside the project directory's hierarchy.
    String rootPath = resourceProvider.convertPath('/root');
    String projectPath = pathContext.join(rootPath, 'project');
    String packageFilePath = pathContext.join(rootPath, 'child', '.packages');
    resourceProvider.newFolder(projectPath);
    Uri fooUri = convertedDirectoryUri('/pkg/foo');
    Uri barUri = convertedDirectoryUri('/pkg/bar');
    createFile(packageFilePath, '''
foo:$fooUri
bar:$barUri
''');

    builderOptions.defaultPackageFilePath = packageFilePath;
    Packages packages = builder.createPackageMap(projectPath);
    expect(packages, isNotNull);
    Map<String, Uri> map = packages.asMap();
    expect(map, hasLength(2));
    expect(map['foo'], fooUri);
    expect(map['bar'], barUri);
  }

  void test_createPackageMap_fromPackageFile_inParentOfRoot() {
    // Use a package file that is inside the parent of the project directory.
    String rootPath = resourceProvider.convertPath('/root');
    String projectPath = pathContext.join(rootPath, 'project');
    String packageFilePath = pathContext.join(rootPath, '.packages');
    resourceProvider.newFolder(projectPath);
    Uri fooUri = convertedDirectoryUri('/pkg/foo');
    Uri barUri = convertedDirectoryUri('/pkg/bar');
    createFile(packageFilePath, '''
foo:$fooUri
bar:$barUri
''');

    Packages packages = builder.createPackageMap(projectPath);
    expect(packages, isNotNull);
    Map<String, Uri> map = packages.asMap();
    expect(map, hasLength(2));
    expect(map['foo'], fooUri);
    expect(map['bar'], barUri);
  }

  void test_createPackageMap_fromPackageFile_inRoot() {
    // Use a package file that is inside the project directory.
    String rootPath = resourceProvider.convertPath('/root');
    String projectPath = pathContext.join(rootPath, 'project');
    String packageFilePath = pathContext.join(projectPath, '.packages');
    resourceProvider.newFolder(projectPath);
    Uri fooUri = convertedDirectoryUri('/pkg/foo');
    Uri barUri = convertedDirectoryUri('/pkg/bar');
    createFile(packageFilePath, '''
foo:$fooUri
bar:$barUri
''');

    Packages packages = builder.createPackageMap(projectPath);
    expect(packages, isNotNull);
    Map<String, Uri> map = packages.asMap();
    expect(map, hasLength(2));
    expect(map['foo'], fooUri);
    expect(map['bar'], barUri);
  }

  void test_createPackageMap_none() {
    String rootPath = resourceProvider.convertPath('/root');
    resourceProvider.newFolder(rootPath);
    Packages packages = builder.createPackageMap(rootPath);
    expect(packages, same(Packages.noPackages));
  }

  void test_createPackageMap_rootDoesNotExist() {
    String rootPath = resourceProvider.convertPath('/root');
    Packages packages = builder.createPackageMap(rootPath);
    expect(packages, same(Packages.noPackages));
  }

  void test_createSourceFactory_bazelWorkspace_fileProvider() {
    String _p(String path) => resourceProvider.convertPath(path);

    String projectPath = _p('/workspace/my/module');
    resourceProvider.newFile(_p('/workspace/WORKSPACE'), '');
    resourceProvider.newFolder(_p('/workspace/bazel-bin'));
    resourceProvider.newFolder(_p('/workspace/bazel-genfiles'));
    resourceProvider.newFolder(projectPath);

    AnalysisOptionsImpl options = new AnalysisOptionsImpl();
    SourceFactoryImpl factory =
        builder.createSourceFactory(projectPath, options);
    expect(factory.resolvers,
        contains(predicate((r) => r is BazelFileUriResolver)));
    expect(factory.resolvers,
        contains(predicate((r) => r is BazelPackageUriResolver)));
  }

  void test_createSourceFactory_bazelWorkspace_withPackagesFile() {
    String _p(String path) => resourceProvider.convertPath(path);

    String projectPath = _p('/workspace/my/module');
    resourceProvider.newFile(_p('/workspace/WORKSPACE'), '');
    resourceProvider.newFolder(_p('/workspace/bazel-bin'));
    resourceProvider.newFolder(_p('/workspace/bazel-genfiles'));
    resourceProvider.newFolder(projectPath);
    resourceProvider.newFile(_p(path.join(projectPath, '.packages')), '');

    AnalysisOptionsImpl options = new AnalysisOptionsImpl();
    SourceFactoryImpl factory =
        builder.createSourceFactory(projectPath, options);
    expect(factory.resolvers,
        contains(predicate((r) => r is ResourceUriResolver)));
    expect(factory.resolvers,
        contains(predicate((r) => r is PackageMapUriResolver)));
  }

  void test_createSourceFactory_noProvider_packages_embedder_extensions() {
    String rootPath = resourceProvider.convertPath('/root');
    Folder rootFolder = resourceProvider.getFolder(rootPath);
    createDefaultSdk(rootFolder);
    String projectPath = pathContext.join(rootPath, 'project');
    String packageFilePath = pathContext.join(projectPath, '.packages');
    String packageA = pathContext.join(rootPath, 'pkgs', 'a');
    String embedderPath = pathContext.join(packageA, '_embedder.yaml');
    String packageB = pathContext.join(rootPath, 'pkgs', 'b');
    String extensionPath = pathContext.join(packageB, '_sdkext');
    createFile(packageFilePath, '''
a:${pathContext.toUri(packageA)}
b:${pathContext.toUri(packageB)}
''');
    String asyncPath = pathContext.join(packageA, 'sdk', 'async.dart');
    String corePath = pathContext.join(packageA, 'sdk', 'core.dart');
    createFile(embedderPath, '''
embedded_libs:
  "dart:async": ${_relativeUri(asyncPath, from: packageA)}
  "dart:core": ${_relativeUri(corePath, from: packageA)}
''');
    String fooPath = pathContext.join(packageB, 'ext', 'foo.dart');
    createFile(extensionPath, '''{
"dart:foo": "${_relativeUri(fooPath, from: packageB)}"
}''');
    AnalysisOptionsImpl options = new AnalysisOptionsImpl();

    SourceFactory factory = builder.createSourceFactory(projectPath, options);

    Source asyncSource = factory.forUri('dart:async');
    expect(asyncSource, isNotNull);
    expect(asyncSource.fullName, asyncPath);

    Source fooSource = factory.forUri('dart:foo');
    expect(fooSource, isNotNull);
    expect(fooSource.fullName, fooPath);

    Source packageSource = factory.forUri('package:b/b.dart');
    expect(packageSource, isNotNull);
    expect(packageSource.fullName, pathContext.join(packageB, 'b.dart'));
  }

  void test_createSourceFactory_noProvider_packages_embedder_noExtensions() {
    String rootPath = resourceProvider.convertPath('/root');
    Folder rootFolder = resourceProvider.getFolder(rootPath);
    createDefaultSdk(rootFolder);
    String projectPath = pathContext.join(rootPath, 'project');
    String packageFilePath = pathContext.join(projectPath, '.packages');
    String packageA = pathContext.join(rootPath, 'pkgs', 'a');
    String embedderPath = pathContext.join(packageA, '_embedder.yaml');
    String packageB = pathContext.join(rootPath, 'pkgs', 'b');
    createFile(packageFilePath, '''
a:${pathContext.toUri(packageA)}
b:${pathContext.toUri(packageB)}
''');
    String asyncPath = pathContext.join(packageA, 'sdk', 'async.dart');
    String corePath = pathContext.join(packageA, 'sdk', 'core.dart');
    createFile(embedderPath, '''
embedded_libs:
  "dart:async": ${_relativeUri(asyncPath, from: packageA)}
  "dart:core": ${_relativeUri(corePath, from: packageA)}
''');
    AnalysisOptionsImpl options = new AnalysisOptionsImpl();

    SourceFactory factory = builder.createSourceFactory(projectPath, options);

    Source dartSource = factory.forUri('dart:async');
    expect(dartSource, isNotNull);
    expect(dartSource.fullName, asyncPath);

    Source packageSource = factory.forUri('package:b/b.dart');
    expect(packageSource, isNotNull);
    expect(packageSource.fullName, pathContext.join(packageB, 'b.dart'));
  }

  @failingTest
  void test_createSourceFactory_noProvider_packages_noEmbedder_extensions() {
    fail('Incomplete test');
  }

  void test_createSourceFactory_noProvider_packages_noEmbedder_noExtensions() {
    String rootPath = resourceProvider.convertPath('/root');
    Folder rootFolder = resourceProvider.getFolder(rootPath);
    createDefaultSdk(rootFolder);
    String projectPath = pathContext.join(rootPath, 'project');
    String packageFilePath = pathContext.join(projectPath, '.packages');
    String packageA = pathContext.join(rootPath, 'pkgs', 'a');
    String packageB = pathContext.join(rootPath, 'pkgs', 'b');
    createFile(packageFilePath, '''
a:${pathContext.toUri(packageA)}
b:${pathContext.toUri(packageB)}
''');
    AnalysisOptionsImpl options = new AnalysisOptionsImpl();

    SourceFactory factory = builder.createSourceFactory(projectPath, options);

    Source dartSource = factory.forUri('dart:core');
    expect(dartSource, isNotNull);
    expect(dartSource.fullName,
        pathContext.join(defaultSdkPath, 'lib', 'core', 'core.dart'));

    Source packageSource = factory.forUri('package:a/a.dart');
    expect(packageSource, isNotNull);
    expect(packageSource.fullName, pathContext.join(packageA, 'a.dart'));
  }

  void test_declareVariables_emptyMap() {
    AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
    Iterable<String> expected = context.declaredVariables.variableNames;
    builderOptions.declaredVariables = <String, String>{};

    builder.declareVariables(context);
    expect(context.declaredVariables.variableNames, unorderedEquals(expected));
  }

  void test_declareVariables_nonEmptyMap() {
    AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
    List<String> expected = context.declaredVariables.variableNames.toList();
    expect(expected, isNot(contains('a')));
    expect(expected, isNot(contains('b')));
    expected.addAll(['a', 'b']);
    builderOptions.declaredVariables = <String, String>{'a': 'a', 'b': 'b'};

    builder.declareVariables(context);
    expect(context.declaredVariables.variableNames, unorderedEquals(expected));
  }

  void test_declareVariables_null() {
    AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
    Iterable<String> expected = context.declaredVariables.variableNames;

    builder.declareVariables(context);
    expect(context.declaredVariables.variableNames, unorderedEquals(expected));
  }

  @failingTest
  void test_findSdk_embedder_extensions() {
    // See test_createSourceFactory_noProvider_packages_embedder_extensions
    fail('Incomplete test');
  }

  @failingTest
  void test_findSdk_embedder_noExtensions() {
    // See test_createSourceFactory_noProvider_packages_embedder_noExtensions
    fail('Incomplete test');
  }

  @failingTest
  void test_findSdk_noEmbedder_extensions() {
    // See test_createSourceFactory_noProvider_packages_noEmbedder_extensions
    fail('Incomplete test');
  }

  @failingTest
  void test_findSdk_noEmbedder_noExtensions() {
    // See test_createSourceFactory_noProvider_packages_noEmbedder_noExtensions
    fail('Incomplete test');
  }

  void test_findSdk_noPackageMap() {
    DartSdk sdk = builder.findSdk(null, new AnalysisOptionsImpl());
    expect(sdk, isNotNull);
  }

  void test_findSdk_noPackageMap_html_strong() {
    DartSdk sdk = builder.findSdk(null, new AnalysisOptionsImpl());
    expect(sdk, isNotNull);
    Source htmlSource = sdk.mapDartUri('dart:html');
    expect(
        htmlSource.fullName,
        resourceProvider
            .convertPath('/sdk/lib/html/dart2js/html_dart2js.dart'));
    expect(htmlSource.exists(), isTrue);
  }

  void test_getAnalysisOptions_default_bazel() {
    _defineMockLintRules();
    AnalysisOptionsImpl defaultOptions = new AnalysisOptionsImpl();
    builderOptions.defaultOptions = defaultOptions;
    AnalysisOptionsImpl expected = new AnalysisOptionsImpl();
    expected.lint = true;
    expected.lintRules = <Linter>[_mockLintRule];
    createFile(resourceProvider.convertPath('/root/WORKSPACE'), '');
    createFile(
        resourceProvider
            .convertPath('/root/dart/analysis_options/lib/default.yaml'),
        '''
linter:
  rules:
    - mock_lint_rule
''');
    createFile(
        resourceProvider
            .convertPath('/root/dart/analysis_options/lib/flutter.yaml'),
        '''
linter:
  rules:
    - mock_lint_rule2
''');
    AnalysisOptions options = builder
        .getAnalysisOptions(resourceProvider.convertPath('/root/some/path'));
    _expectEqualOptions(options, expected);
  }

  void test_getAnalysisOptions_default_flutter() {
    _defineMockLintRules();
    AnalysisOptionsImpl defaultOptions = new AnalysisOptionsImpl();
    builderOptions.defaultOptions = defaultOptions;
    AnalysisOptionsImpl expected = new AnalysisOptionsImpl();
    expected.lint = true;
    expected.lintRules = <Linter>[_mockLintRule];
    String packagesFilePath =
        resourceProvider.convertPath('/some/directory/path/.packages');
    createFile(packagesFilePath, 'flutter:/pkg/flutter/lib/');
    String optionsFilePath = resourceProvider
        .convertPath('/pkg/flutter/lib/analysis_options_user.yaml');
    createFile(optionsFilePath, '''
linter:
  rules:
    - mock_lint_rule
''');
    String projPath = resourceProvider.convertPath('/some/directory/path');
    AnalysisOptions options = builder.getAnalysisOptions(projPath);
    _expectEqualOptions(options, expected);
  }

  void test_getAnalysisOptions_default_noOverrides() {
    AnalysisOptionsImpl defaultOptions = new AnalysisOptionsImpl();
    defaultOptions.enableLazyAssignmentOperators = true;
    builderOptions.defaultOptions = defaultOptions;
    AnalysisOptionsImpl expected = new AnalysisOptionsImpl();
    expected.enableLazyAssignmentOperators = true;
    String path = resourceProvider.convertPath('/some/directory/path');
    String filePath =
        pathContext.join(path, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
    resourceProvider.newFile(filePath, '''
linter:
  rules:
    - empty_constructor_bodies
''');

    AnalysisOptions options = builder.getAnalysisOptions(path);
    _expectEqualOptions(options, expected);
  }

  void test_getAnalysisOptions_default_overrides() {
    AnalysisOptionsImpl defaultOptions = new AnalysisOptionsImpl();
    defaultOptions.enableSuperMixins = false;
    defaultOptions.enableLazyAssignmentOperators = true;
    builderOptions.defaultOptions = defaultOptions;
    AnalysisOptionsImpl expected = new AnalysisOptionsImpl();
    expected.enableSuperMixins = true;
    expected.enableLazyAssignmentOperators = true;
    String path = resourceProvider.convertPath('/some/directory/path');
    String filePath =
        pathContext.join(path, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
    resourceProvider.newFile(filePath, '''
analyzer:
  language:
    enableSuperMixins : true
''');

    AnalysisOptions options = builder.getAnalysisOptions(path);
    _expectEqualOptions(options, expected);
  }

  void test_getAnalysisOptions_gnWorkspace() {
    String _p(String path) => resourceProvider.convertPath(path);
    String projectPath = _p('/workspace/some/path');
    resourceProvider.newFolder(_p('/workspace/.jiri_root'));
    resourceProvider.newFile(
        _p('/workspace/out/debug/gen/dart.sources/foo_pkg'),
        _p('/workspace/foo_pkg/lib'));
    resourceProvider.newFolder(projectPath);
    ArgParser argParser = new ArgParser();
    defineAnalysisArguments(argParser);
    ArgResults argResults = argParser.parse([]);
    builderOptions = createContextBuilderOptions(argResults);
    builder = new ContextBuilder(resourceProvider, sdkManager, contentCache,
        options: builderOptions);
    AnalysisOptionsImpl expected = new AnalysisOptionsImpl();
    AnalysisOptions options = builder.getAnalysisOptions(projectPath);
    _expectEqualOptions(options, expected);
  }

  void test_getAnalysisOptions_includes() {
    _defineMockLintRules();
    AnalysisOptionsImpl defaultOptions = new AnalysisOptionsImpl();
    defaultOptions.enableSuperMixins = false;
    builderOptions.defaultOptions = defaultOptions;
    AnalysisOptionsImpl expected = new AnalysisOptionsImpl();
    expected.enableSuperMixins = true;
    expected.lint = true;
    expected.lintRules = <Linter>[
      _mockLintRule,
      _mockLintRule2,
      _mockLintRule3
    ];
    resourceProvider.newFile(
        resourceProvider.convertPath('/mypkgs/somepkg/lib/here.yaml'), '''
linter:
  rules:
    - mock_lint_rule3
''');
    String path = resourceProvider.convertPath('/some/directory/path');
    resourceProvider.newFile(pathContext.join(path, '.packages'), '''
somepkg:../../../mypkgs/somepkg/lib
''');
    resourceProvider.newFile(pathContext.join(path, 'bar.yaml'), '''
include: package:somepkg/here.yaml
analyzer:
  language:
    enableSuperMixins : true
linter:
  rules:
    - mock_lint_rule2
''');
    String filePath =
        pathContext.join(path, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
    resourceProvider.newFile(filePath, '''
include: bar.yaml
linter:
  rules:
    - mock_lint_rule
''');

    AnalysisOptions options = builder.getAnalysisOptions(path);
    _expectEqualOptions(options, expected);
  }

  void test_getAnalysisOptions_invalid() {
    String path = resourceProvider.convertPath('/some/directory/path');
    String filePath =
        pathContext.join(path, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
    resourceProvider.newFile(filePath, ';');

    AnalysisOptions options = builder.getAnalysisOptions(path);
    expect(options, isNotNull);
  }

  void test_getAnalysisOptions_noDefault_noOverrides() {
    String path = resourceProvider.convertPath('/some/directory/path');
    String filePath =
        pathContext.join(path, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
    resourceProvider.newFile(filePath, '''
linter:
  rules:
    - empty_constructor_bodies
''');

    AnalysisOptions options = builder.getAnalysisOptions(path);
    _expectEqualOptions(options, new AnalysisOptionsImpl());
  }

  void test_getAnalysisOptions_noDefault_overrides() {
    AnalysisOptionsImpl expected = new AnalysisOptionsImpl();
    expected.enableSuperMixins = true;
    String path = resourceProvider.convertPath('/some/directory/path');
    String filePath =
        pathContext.join(path, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
    resourceProvider.newFile(filePath, '''
analyzer:
  language:
    enableSuperMixins : true
''');

    AnalysisOptions options = builder.getAnalysisOptions(path);
    _expectEqualOptions(options, expected);
  }

  void test_getAnalysisOptions_optionsPath() {
    String path = resourceProvider.convertPath('/some/directory/path');
    String filePath =
        pathContext.join(path, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
    resourceProvider.newFile(filePath, '''
linter:
  rules:
    - empty_constructor_bodies
''');

    ContextRoot root = new ContextRoot(path, [], pathContext: pathContext);
    builder.getAnalysisOptions(path, contextRoot: root);
    expect(root.optionsFilePath, equals(filePath));
  }

  void test_getOptionsFile_explicit() {
    String path = resourceProvider.convertPath('/some/directory/path');
    String filePath = resourceProvider.convertPath('/options/analysis.yaml');
    resourceProvider.newFile(filePath, '');

    builderOptions.defaultAnalysisOptionsFilePath = filePath;
    File result = builder.getOptionsFile(path);
    expect(result, isNotNull);
    expect(result.path, filePath);
  }

  void test_getOptionsFile_inParentOfRoot_new() {
    String parentPath = resourceProvider.convertPath('/some/directory');
    String path = pathContext.join(parentPath, 'path');
    String filePath =
        pathContext.join(parentPath, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
    resourceProvider.newFile(filePath, '');

    File result = builder.getOptionsFile(path);
    expect(result, isNotNull);
    expect(result.path, filePath);
  }

  void test_getOptionsFile_inParentOfRoot_old() {
    String parentPath = resourceProvider.convertPath('/some/directory');
    String path = pathContext.join(parentPath, 'path');
    String filePath =
        pathContext.join(parentPath, AnalysisEngine.ANALYSIS_OPTIONS_FILE);
    resourceProvider.newFile(filePath, '');

    File result = builder.getOptionsFile(path);
    expect(result, isNotNull);
    expect(result.path, filePath);
  }

  void test_getOptionsFile_inRoot_new() {
    String path = resourceProvider.convertPath('/some/directory/path');
    String filePath =
        pathContext.join(path, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
    resourceProvider.newFile(filePath, '');

    File result = builder.getOptionsFile(path);
    expect(result, isNotNull);
    expect(result.path, filePath);
  }

  void test_getOptionsFile_inRoot_old() {
    String path = resourceProvider.convertPath('/some/directory/path');
    String filePath =
        pathContext.join(path, AnalysisEngine.ANALYSIS_OPTIONS_FILE);
    resourceProvider.newFile(filePath, '');

    File result = builder.getOptionsFile(path);
    expect(result, isNotNull);
    expect(result.path, filePath);
  }

  _defineMockLintRules() {
    _mockLintRule = new _MockLintRule('mock_lint_rule');
    Registry.ruleRegistry.registerDefault(_mockLintRule);
    _mockLintRule2 = new _MockLintRule('mock_lint_rule2');
    Registry.ruleRegistry.registerDefault(_mockLintRule2);
    _mockLintRule3 = new _MockLintRule('mock_lint_rule3');
    Registry.ruleRegistry.register(_mockLintRule3);
    _mockPublicMemberApiDocs = new _MockLintRule('public_member_api_docs');
    Registry.ruleRegistry.register(_mockPublicMemberApiDocs);
  }

  void _expectEqualOptions(
      AnalysisOptionsImpl actual, AnalysisOptionsImpl expected) {
    // TODO(brianwilkerson) Consider moving this to AnalysisOptionsImpl.==.
    expect(actual.analyzeFunctionBodiesPredicate,
        same(expected.analyzeFunctionBodiesPredicate));
    expect(actual.dart2jsHint, expected.dart2jsHint);
    expect(actual.enableLazyAssignmentOperators,
        expected.enableLazyAssignmentOperators);
    expect(actual.enableSuperMixins, expected.enableSuperMixins);
    expect(actual.enableTiming, expected.enableTiming);
    expect(actual.generateImplicitErrors, expected.generateImplicitErrors);
    expect(actual.generateSdkErrors, expected.generateSdkErrors);
    expect(actual.hint, expected.hint);
    expect(actual.lint, expected.lint);
    expect(
      actual.lintRules.map((l) => l.name),
      unorderedEquals(expected.lintRules.map((l) => l.name)),
    );
    expect(actual.preserveComments, expected.preserveComments);
    expect(actual.strongMode, expected.strongMode);
    expect(actual.strongModeHints, expected.strongModeHints);
    expect(actual.declarationCasts, expected.declarationCasts);
    expect(actual.implicitCasts, expected.implicitCasts);
    expect(actual.implicitDynamic, expected.implicitDynamic);
    expect(actual.trackCacheDependencies, expected.trackCacheDependencies);
    expect(actual.disableCacheFlushing, expected.disableCacheFlushing);
  }

  Uri _relativeUri(String path, {String from}) {
    String relativePath = pathContext.relative(path, from: from);
    return pathContext.toUri(relativePath);
  }
}

@reflectiveTest
class EmbedderYamlLocatorTest extends EmbedderRelatedTest {
  void test_empty() {
    EmbedderYamlLocator locator = new EmbedderYamlLocator({
      'fox': <Folder>[pathTranslator.getResource(emptyPath)]
    });
    expect(locator.embedderYamls, hasLength(0));
  }

  void test_invalid() {
    EmbedderYamlLocator locator = new EmbedderYamlLocator(null);
    locator.addEmbedderYaml(null, r'''{{{,{{}}},}}''');
    expect(locator.embedderYamls, hasLength(0));
  }

  void test_valid() {
    EmbedderYamlLocator locator = new EmbedderYamlLocator({
      'fox': <Folder>[pathTranslator.getResource(foxLib)]
    });
    expect(locator.embedderYamls, hasLength(1));
  }
}

class _MockLintRule implements LintRule {
  final String _name;

  _MockLintRule(this._name);

  @override
  String get name => _name;

  noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
