<commit_before>// Copyright (c) 2015, 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.

library test.analysis.updateContent;

import 'package:analysis_server/plugin/protocol/protocol.dart';
import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/constants.dart';
import 'package:analysis_server/src/services/index/index.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'package:typed_mock/typed_mock.dart';
import 'package:unittest/unittest.dart';

import '../analysis_abstract.dart';
import '../utils.dart';

main() {
  initializeTestEnvironment();
  defineReflectiveTests(UpdateContentTest);
}

compilationUnitMatcher(String file) {
  return new _ArgumentMatcher_CompilationUnit(file);
}

@reflectiveTest
class UpdateContentTest extends AbstractAnalysisTest {
  Map<String, List<AnalysisError>> filesErrors = {};
  int serverErrorCount = 0;
  int navigationCount = 0;

  Index createIndex() {
    return new _MockIndex();
  }

  @override
  void processNotification(Notification notification) {
    if (notification.event == ANALYSIS_ERRORS) {
      var decoded = new AnalysisErrorsParams.fromNotification(notification);
      filesErrors[decoded.file] = decoded.errors;
    }
    if (notification.event == ANALYSIS_NAVIGATION) {
      navigationCount++;
    }
    if (notification.event == SERVER_ERROR) {
      serverErrorCount++;
    }
  }

  test_discardNotifications_onSourceChange() async {
    createProject();
    addTestFile('');
    await server.onAnalysisComplete;
    server.setAnalysisSubscriptions({
      AnalysisService.NAVIGATION: [testFile].toSet()
    });
    // update file, analyze, but don't sent notifications
    navigationCount = 0;
    server.updateContent('1', {testFile: new AddContentOverlay('foo() {}')});
    server.test_performAllAnalysisOperations();
    expect(serverErrorCount, 0);
    expect(navigationCount, 0);
    // replace the file contents,
    // should discard any pending notification operations
    server.updateContent('2', {testFile: new AddContentOverlay('bar() {}')});
    await server.onAnalysisComplete;
    expect(serverErrorCount, 0);
    expect(navigationCount, 1);
  }

  test_illegal_ChangeContentOverlay() {
    // It should be illegal to send a ChangeContentOverlay for a file that
    // doesn't have an overlay yet.
    createProject();
    addTestFile('library foo;');
    String id = 'myId';
    try {
      server.updateContent(id, {
        testFile: new ChangeContentOverlay([new SourceEdit(8, 3, 'bar')])
      });
      fail('Expected an exception to be thrown');
    } on RequestFailure catch (e) {
      expect(e.response.id, id);
      expect(e.response.error.code, RequestErrorCode.INVALID_OVERLAY_CHANGE);
    }
  }

  test_indexUnitAfterNopChange() async {
    var testUnitMatcher = compilationUnitMatcher(testFile) as dynamic;
    createProject();
    addTestFile('main() { print(1); }');
    await server.onAnalysisComplete;
    verify(server.index.index(anyObject, testUnitMatcher)).times(1);
    // add an overlay
    server.updateContent(
        '1', {testFile: new AddContentOverlay('main() { print(2); }')});
    // Perform the next single operation: analysis.
    // It will schedule an indexing operation.
    await server.test_onOperationPerformed;
    // Update the file and remove an overlay.
    resourceProvider.updateFile(testFile, 'main() { print(2); }');
    server.updateContent('2', {testFile: new RemoveContentOverlay()});
    // Validate that at the end the unit was indexed.
    await server.onAnalysisComplete;
    verify(server.index.index(anyObject, testUnitMatcher)).times(2);
  }

  test_multiple_contexts() async {
    String fooPath = '/project1/foo.dart';
    resourceProvider.newFile(
        fooPath,
        '''
library foo;
import '../project2/baz.dart';<commit_msg>Tweak for make test passing with useTaskModel = true.<commit_after>// Copyright (c) 2015, 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.

library test.analysis.updateContent;

import 'package:analysis_server/plugin/protocol/protocol.dart';
import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/constants.dart';
import 'package:analysis_server/src/services/index/index.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'package:typed_mock/typed_mock.dart';
import 'package:unittest/unittest.dart';

import '../analysis_abstract.dart';
import '../utils.dart';

main() {
  initializeTestEnvironment();
  defineReflectiveTests(UpdateContentTest);
}

compilationUnitMatcher(String file) {
  return new _ArgumentMatcher_CompilationUnit(file);
}

@reflectiveTest
class UpdateContentTest extends AbstractAnalysisTest {
  Map<String, List<AnalysisError>> filesErrors = {};
  int serverErrorCount = 0;
  int navigationCount = 0;

  Index createIndex() {
    return new _MockIndex();
  }

  @override
  void processNotification(Notification notification) {
    if (notification.event == ANALYSIS_ERRORS) {
      var decoded = new AnalysisErrorsParams.fromNotification(notification);
      filesErrors[decoded.file] = decoded.errors;
    }
    if (notification.event == ANALYSIS_NAVIGATION) {
      navigationCount++;
    }
    if (notification.event == SERVER_ERROR) {
      serverErrorCount++;
    }
  }

  test_discardNotifications_onSourceChange() async {
    createProject();
    addTestFile('');
    await server.onAnalysisComplete;
    server.setAnalysisSubscriptions({
      AnalysisService.NAVIGATION: [testFile].toSet()
    });
    // update file, analyze, but don't sent notifications
    navigationCount = 0;
    server.updateContent('1', {testFile: new AddContentOverlay('foo() {}')});
    server.test_performAllAnalysisOperations();
    expect(serverErrorCount, 0);
    expect(navigationCount, 0);
    // replace the file contents,
    // should discard any pending notification operations
    server.updateContent('2', {testFile: new AddContentOverlay('bar() {}')});
    await server.onAnalysisComplete;
    expect(serverErrorCount, 0);
    expect(navigationCount, 1);
  }

  test_illegal_ChangeContentOverlay() {
    // It should be illegal to send a ChangeContentOverlay for a file that
    // doesn't have an overlay yet.
    createProject();
    addTestFile('library foo;');
    String id = 'myId';
    try {
      server.updateContent(id, {
        testFile: new ChangeContentOverlay([new SourceEdit(8, 3, 'bar')])
      });
      fail('Expected an exception to be thrown');
    } on RequestFailure catch (e) {
      expect(e.response.id, id);
      expect(e.response.error.code, RequestErrorCode.INVALID_OVERLAY_CHANGE);
    }
  }

  test_indexUnitAfterNopChange() async {
    var testUnitMatcher = compilationUnitMatcher(testFile) as dynamic;
    createProject();
    addTestFile('main() { print(1); }');
    await server.onAnalysisComplete;
    verify(server.index.index(anyObject, testUnitMatcher)).times(1);
    // add an overlay
    server.updateContent(
        '1', {testFile: new AddContentOverlay('main() { print(2); }')});
    // Perform the next single operation: analysis.
    // It will schedule an indexing operation.
    await server.test_onOperationPerformed;
    // Update the file and remove an overlay.
    resourceProvider.updateFile(testFile, 'main() { print(2); }');
    server.updateContent('2', {testFile: new RemoveContentOverlay()});
    // Validate that at the end the unit was indexed.
    await server.onAnalysisComplete;
    if (AnalysisEngine.instance.useTaskModel) {
      verify(server.index.index(anyObject, testUnitMatcher)).times(3);
    } else {
      verify(server.index.index(anyObject, testUnitMatcher)).times(2);
    }
  }

  test_multiple_contexts() async {
    String fooPath = '/project1/foo.dart';
    resourceProvider.newFile(
        fooPath,
        '''
library foo;
import '../project2/baz.dart';
{'fraction_non_alphanumeric': 0.11813314711359404, 'fraction_numerical': 0.005702979515828678, 'mean_word_length': 4.176506024096385, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2401255', 'n_tokens_mistral': 2521, 'n_tokens_neox': 2492, 'n_words': 573}