recording_canvas.dart 2.91 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// Copyright 2017 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/rendering.dart';

/// A [Canvas] for tests that records its method calls.
///
/// This class can be used in conjuction with [TestRecordingPaintingContext]
/// to record the [Canvas] method calls made by a renderer. For example:
///
/// ```dart
/// RenderBox box = tester.renderObject(find.text('ABC'));
/// TestRecordingCanvas canvas = new TestRecordingCanvas();
/// TestRecordingPaintingContext context = new TestRecordingPaintingContext(canvas);
/// box.paint(context, Offset.zero);
/// // Now test the expected canvas.invocations.
/// ```
///
/// In some cases it may be useful to define a subclass that overrides the
21
/// [Canvas] methods the test is checking and squirrels away the parameters
22
/// that the test requires.
23 24 25
///
/// For simple tests, consider using the [paints] matcher, which overlays a
/// pattern matching API over [TestRecordingCanvas].
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
class TestRecordingCanvas implements Canvas {
  /// All of the method calls on this canvas.
  final List<Invocation> invocations = <Invocation>[];

  int _saveCount = 0;

  @override
  int getSaveCount() => _saveCount;

  @override
  void save() {
    _saveCount += 1;
    invocations.add(new _MethodCall(#save));
  }

41 42 43 44 45 46
  @override
  void saveLayer(Rect bounds, Paint paint) {
    _saveCount += 1;
    invocations.add(new _MethodCall(#saveLayer, <dynamic>[bounds, paint]));
  }

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
  @override
  void restore() {
    _saveCount -= 1;
    assert(_saveCount >= 0);
    invocations.add(new _MethodCall(#restore));
  }

  @override
  void noSuchMethod(Invocation invocation) {
    invocations.add(invocation);
  }
}

/// A [PaintingContext] for tests that use [TestRecordingCanvas].
class TestRecordingPaintingContext implements PaintingContext {
  /// Creates a [PaintingContext] for tests that use [TestRecordingCanvas].
  TestRecordingPaintingContext(this.canvas);

  @override
  final Canvas canvas;

  @override
  void paintChild(RenderObject child, Offset offset) {
    child.paint(this, offset);
  }

  @override
  void pushClipRect(bool needsCompositing, Offset offset, Rect clipRect, PaintingContextCallback painter) {
    canvas.save();
    canvas.clipRect(clipRect.shift(offset));
    painter(this, offset);
    canvas.restore();
  }

  @override
82
  void noSuchMethod(Invocation invocation) { }
83 84 85
}

class _MethodCall implements Invocation {
86
  _MethodCall(this._name, [ this._arguments = const <dynamic>[] ]);
87
  final Symbol _name;
88
  final List<dynamic> _arguments;
89 90 91 92 93 94 95 96 97 98 99 100 101
  @override
  bool get isAccessor => false;
  @override
  bool get isGetter => false;
  @override
  bool get isMethod => true;
  @override
  bool get isSetter => false;
  @override
  Symbol get memberName => _name;
  @override
  Map<Symbol, dynamic> get namedArguments => <Symbol, dynamic>{};
  @override
102
  List<dynamic> get positionalArguments => _arguments;
103
}