// 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.

import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';

import 'test_widgets.dart';

class ProbeWidget extends StatefulWidget {
  @override
  ProbeWidgetState createState() => new ProbeWidgetState();
}

class ProbeWidgetState extends State<ProbeWidget> {
  static int buildCount = 0;

  @override
  void initState() {
    super.initState();
    setState(() {});
  }

  @override
  void didUpdateConfig(ProbeWidget oldConfig) {
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    setState(() {});
    buildCount++;
    return new Container();
  }
}

class BadWidget extends StatelessWidget {
  BadWidget(this.parentState);

  final BadWidgetParentState parentState;

  @override
  Widget build(BuildContext context) {
    parentState._markNeedsBuild();
    return new Container();
  }
}

class BadWidgetParent extends StatefulWidget {
  @override
  BadWidgetParentState createState() => new BadWidgetParentState();
}

class BadWidgetParentState extends State<BadWidgetParent> {
  void _markNeedsBuild() {
    setState(() {
      // Our state didn't really change, but we're doing something pathological
      // here to trigger an interesting scenario to test.
    });
  }

  @override
  Widget build(BuildContext context) {
    return new BadWidget(this);
  }
}

class BadDisposeWidget extends StatefulWidget {
  @override
  BadDisposeWidgetState createState() => new BadDisposeWidgetState();
}

class BadDisposeWidgetState extends State<BadDisposeWidget> {
  @override
  Widget build(BuildContext context) {
    return new Container();
  }

  @override
  void dispose() {
    setState(() { /* This is invalid behavior. */ });
    super.dispose();
  }
}

void main() {
  testWidgets('Legal times for setState', (WidgetTester tester) async {
    GlobalKey flipKey = new GlobalKey();
    expect(ProbeWidgetState.buildCount, equals(0));
    await tester.pumpWidget(new ProbeWidget());
    expect(ProbeWidgetState.buildCount, equals(1));
    await tester.pumpWidget(new ProbeWidget());
    expect(ProbeWidgetState.buildCount, equals(2));
    await tester.pumpWidget(new FlipWidget(
      key: flipKey,
      left: new Container(),
      right: new ProbeWidget()
    ));
    expect(ProbeWidgetState.buildCount, equals(2));
    FlipWidgetState flipState1 = flipKey.currentState;
    flipState1.flip();
    await tester.pump();
    expect(ProbeWidgetState.buildCount, equals(3));
    FlipWidgetState flipState2 = flipKey.currentState;
    flipState2.flip();
    await tester.pump();
    expect(ProbeWidgetState.buildCount, equals(3));
    await tester.pumpWidget(new Container());
    expect(ProbeWidgetState.buildCount, equals(3));
  });

  testWidgets('Setting parent state during build is forbidden', (WidgetTester tester) async {
    await tester.pumpWidget(new BadWidgetParent());
    expect(tester.takeException(), isNotNull);
    await tester.pumpWidget(new Container());
  });

  testWidgets('Setting state during dispose is forbidden', (WidgetTester tester) async {
    await tester.pumpWidget(new BadDisposeWidget());
    expect(tester.takeException(), isNull);
    await tester.pumpWidget(new Container());
    expect(tester.takeException(), isNotNull);
  });
}