Add flutter analysis tests (#585)

diff --git a/test/all.dart b/test/all.dart
index 4fb2e80..a50d34b 100644
--- a/test/all.dart
+++ b/test/all.dart
@@ -11,6 +11,7 @@
 import 'common_server_api_test.dart' as common_server_api_test;
 import 'common_test.dart' as common_test;
 import 'compiler_test.dart' as compiler_test;
+import 'flutter_analysis_server_test.dart' as flutter_analysis_server_test;
 import 'flutter_web_test.dart' as flutter_web_test;
 import 'gae_deployed_test.dart' as gae_deployed_test;
 import 'pub_test.dart' as pub_test;
@@ -25,6 +26,7 @@
   common_server_api_protobuf_test.defineTests();
   common_test.defineTests();
   compiler_test.defineTests();
+  flutter_analysis_server_test.defineTests();
   flutter_web_test.defineTests();
   gae_deployed_test.defineTests();
   pub_test.defineTests();
diff --git a/test/flutter_analysis_server_test.dart b/test/flutter_analysis_server_test.dart
new file mode 100644
index 0000000..17b5d04
--- /dev/null
+++ b/test/flutter_analysis_server_test.dart
@@ -0,0 +1,230 @@
+// 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 services.flutter_analyzer_server_test;
+
+import 'package:dart_services/src/analysis_server.dart';
+import 'package:dart_services/src/protos/dart_services.pb.dart' as proto;
+import 'package:dart_services/src/flutter_web.dart';
+import 'package:dart_services/src/sdk_manager.dart';
+import 'package:test/test.dart';
+
+const counter = r'''
+// Copyright (c) 2019, 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:flutter/material.dart';
+
+void main() => runApp(MyApp());
+
+class MyApp extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return MaterialApp(
+      title: 'Flutter Demo',
+      debugShowCheckedModeBanner: false,
+      theme: ThemeData(
+        primarySwatch: Colors.blue,
+      ),
+      home: MyHomePage(title: 'Flutter Demo Home Page'),
+    );
+  }
+}
+
+class MyHomePage extends StatefulWidget {
+  MyHomePage({Key key, this.title}) : super(key: key);
+
+  final String title;
+
+  @override
+  _MyHomePageState createState() => _MyHomePageState();
+}
+
+class _MyHomePageState extends State<MyHomePage> {
+  int _counter = 0;
+
+  void _incrementCounter() {
+    setState(() {
+      _counter++;
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: Text(widget.title),
+      ),
+      body: Center(
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[
+            Text(
+              'You have pushed the button this many times:',
+            ),
+            Text(
+              '$_counter',
+              style: Theme.of(context).textTheme.headline4,
+            ),
+          ],
+        ),
+      ),
+      floatingActionButton: FloatingActionButton(
+        onPressed: _incrementCounter,
+        tooltip: 'Increment',
+        child: Icon(Icons.add),
+      ),
+    );
+  }
+}
+''';
+
+const draggableAndPhysics = '''
+import 'package:flutter/material.dart';
+import 'package:flutter/physics.dart';
+
+main() {
+  runApp(
+    MaterialApp(
+      debugShowCheckedModeBanner: false,
+      home: PhysicsCardDragDemo(),
+    ),
+  );
+}
+
+class PhysicsCardDragDemo extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: Text('A draggable card!'),
+      ),
+      body: DraggableCard(
+        child: FlutterLogo(
+          size: 128,
+        ),
+      ),
+    );
+  }
+}
+
+class DraggableCard extends StatefulWidget {
+  final Widget child;
+  DraggableCard({this.child});
+
+  @override
+  _DraggableCardState createState() => _DraggableCardState();
+}
+
+class _DraggableCardState extends State<DraggableCard>
+    with SingleTickerProviderStateMixin {
+  AnimationController _controller;
+  Alignment _dragAlignment = Alignment.center;
+  Animation<Alignment> _animation;
+
+  void _runAnimation(Offset pixelsPerSecond, Size size) {
+    _animation = _controller.drive(
+      AlignmentTween(
+        begin: _dragAlignment,
+        end: Alignment.center,
+      ),
+    );
+
+    final unitsPerSecondX = pixelsPerSecond.dx / size.width;
+    final unitsPerSecondY = pixelsPerSecond.dy / size.height;
+    final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY);
+    final unitVelocity = unitsPerSecond.distance;
+
+    const spring = SpringDescription(
+      mass: 30,
+      stiffness: 1,
+      damping: 1,
+    );
+
+    final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);
+
+    _controller.animateWith(simulation);
+  }
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = AnimationController(vsync: this);
+
+    _controller.addListener(() {
+      setState(() {
+        _dragAlignment = _animation.value;
+      });
+    });
+  }
+
+  @override
+  void dispose() {
+    _controller.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final size = MediaQuery.of(context).size;
+    return GestureDetector(
+      onPanDown: (details) {
+        _controller.stop();
+      },
+      onPanUpdate: (details) {
+        setState(() {
+          _dragAlignment += Alignment(
+            details.delta.dx / (size.width / 2),
+            details.delta.dy / (size.height / 2),
+          );
+        });
+      },
+      onPanEnd: (details) {
+        _runAnimation(details.velocity.pixelsPerSecond, size);
+      },
+      child: Align(
+        alignment: _dragAlignment,
+        child: Card(
+          child: widget.child,
+        ),
+      ),
+    );
+  }
+}
+''';
+
+void main() => defineTests();
+
+void defineTests() {
+  AnalysisServerWrapper analysisServer;
+  FlutterWebManager flutterWebManager;
+
+  group('Flutter SDK analysis_server', () {
+    setUp(() async {
+      flutterWebManager = FlutterWebManager(SdkManager.flutterSdk);
+      await flutterWebManager.initFlutterWeb();
+      analysisServer = AnalysisServerWrapper(
+          SdkManager.flutterSdk.sdkPath, flutterWebManager);
+      await analysisServer.init();
+      await analysisServer.warmup();
+    });
+
+    tearDown(() => analysisServer.shutdown());
+
+    test('analyze counter app', () async {
+      final results = await analysisServer.analyze(counter);
+      expect(results.issues, isEmpty);
+    });
+
+    test('analyze Draggable Physics sample', () async {
+      final results = await analysisServer.analyze(draggableAndPhysics);
+      expect(results.issues, isEmpty);
+    });
+  });
+}
+
+bool completionsContains(proto.CompleteResponse response, String expected) =>
+    response.completions
+        .any((completion) => completion.completion['completion'] == expected);