// Copyright (c) 2014, 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 'dart:mirrors'; import 'package:analysis_server/src/protocol_server.dart'; import 'package:analysis_server/src/services/search/search_engine.dart'; import 'package:analyzer/dart/ast/ast.dart' as engine; import 'package:analyzer/dart/element/element.dart' as engine; import 'package:analyzer/dart/element/type.dart' as engine; import 'package:analyzer/error/error.dart' as engine; import 'package:analyzer/src/error/codes.dart' as engine; import 'package:analyzer/src/generated/source.dart' as engine; import 'package:analyzer_plugin/protocol/protocol_common.dart'; import 'package:test/test.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; import 'constants.dart'; import 'mocks.dart'; main() { defineReflectiveSuite(() { defineReflectiveTests(AnalysisErrorTest); defineReflectiveTests(EnumTest); }); } @reflectiveTest class AnalysisErrorTest { MockSource source = new MockSource(); engine.LineInfo lineInfo; MockAnalysisError engineError; void setUp() { // prepare Source source.fullName = 'foo.dart'; // prepare LineInfo lineInfo = new engine.LineInfo([0, 5, 9, 20]); // prepare AnalysisError engineError = new MockAnalysisError(source, engine.CompileTimeErrorCode.AMBIGUOUS_EXPORT, 10, 20, 'my message'); } void tearDown() { source = null; engineError = null; } void test_fromEngine_hasCorrection() { engineError.correction = 'my correction'; AnalysisError error = newAnalysisError_fromEngine(lineInfo, engineError); expect(error.toJson(), { SEVERITY: 'ERROR', TYPE: 'COMPILE_TIME_ERROR', LOCATION: { FILE: 'foo.dart', OFFSET: 10, LENGTH: 20, START_LINE: 3, START_COLUMN: 2 }, MESSAGE: 'my message', CORRECTION: 'my correction', CODE: 'ambiguous_export', HAS_FIX: false }); } void test_fromEngine_noCorrection() { engineError.correction = null; AnalysisError error = newAnalysisError_fromEngine(lineInfo, engineError); expect(error.toJson(), { SEVERITY: 'ERROR', TYPE: 'COMPILE_TIME_ERROR', LOCATION: { FILE: 'foo.dart', OFFSET: 10, LENGTH: 20, START_LINE: 3, START_COLUMN: 2 }, MESSAGE: 'my message', CODE: 'ambiguous_export', HAS_FIX: false }); } void test_fromEngine_noLineInfo() { engineError.correction = null; AnalysisError error = newAnalysisError_fromEngine(null, engineError); expect(error.toJson(), { SEVERITY: 'ERROR', TYPE: 'COMPILE_TIME_ERROR', LOCATION: { FILE: 'foo.dart', OFFSET: 10, LENGTH: 20, START_LINE: -1, START_COLUMN: -1 }, MESSAGE: 'my message', CODE: 'ambiguous_export', HAS_FIX: false }); } } @reflectiveTest class EnumTest { void test_AnalysisErrorSeverity() { new EnumTester().run( (engine.ErrorSeverity engineErrorSeverity) => new AnalysisErrorSeverity(engineErrorSeverity.name), exceptions: {engine.ErrorSeverity.NONE: null}); } void test_AnalysisErrorType() { new EnumTester().run( (engine.ErrorType engineErrorType) => new AnalysisErrorType(engineErrorType.name)); } void test_ElementKind() { new EnumTester() .run(convertElementKind, exceptions: { // TODO(paulberry): do any of the exceptions below constitute bugs? engine.ElementKind.DYNAMIC: ElementKind.UNKNOWN, engine.ElementKind.ERROR: ElementKind.UNKNOWN, engine.ElementKind.EXPORT: ElementKind.UNKNOWN, engine.ElementKind.GENERIC_FUNCTION_TYPE: ElementKind.UNKNOWN, engine.ElementKind.IMPORT: ElementKind.UNKNOWN, engine.ElementKind.NAME: ElementKind.UNKNOWN, engine.ElementKind.UNIVERSE: ElementKind.UNKNOWN }); } void test_SearchResultKind() { // TODO(paulberry): why does the MatchKind class exist at all? Can't we // use SearchResultKind inside the analysis server? new EnumTester() .run(newSearchResultKind_fromEngine); } } /** * Helper class for testing the correspondence between an analysis engine enum * and an analysis server API enum. */ class EnumTester { /** * Test that the function [convert] properly converts all possible values of * [EngineEnum] to an [ApiEnum] with the same name, with the exceptions noted * in [exceptions]. For each key in [exceptions], if the corresponding value * is null, then we check that converting the given key results in an error. * If the corresponding value is an [ApiEnum], then we check that converting * the given key results in the given value. */ void run(ApiEnum convert(EngineEnum value), {Map exceptions: const {}}) { ClassMirror engineClass = reflectClass(EngineEnum); engineClass.staticMembers.forEach((Symbol symbol, MethodMirror method) { if (symbol == #values) { return; } if (!method.isGetter) { return; } String enumName = MirrorSystem.getName(symbol); EngineEnum engineValue = engineClass.getField(symbol).reflectee as EngineEnum; expect(engineValue, new TypeMatcher()); if (exceptions.containsKey(engineValue)) { ApiEnum expectedResult = exceptions[engineValue]; if (expectedResult == null) { expect(() { convert(engineValue); }, throwsException); } else { ApiEnum apiValue = convert(engineValue); expect(apiValue, equals(expectedResult)); } } else { ApiEnum apiValue = convert(engineValue); expect((apiValue as dynamic).name, equals(enumName)); } }); } } class MockAnalysisError implements engine.AnalysisError { @override MockSource source; @override engine.ErrorCode errorCode; @override int offset; @override String message; @override String correction = null; @override bool isStaticOnly; @override int length; MockAnalysisError( this.source, this.errorCode, this.offset, this.length, this.message); }