// Copyright 2014 The Flutter 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/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';

import 'restoration.dart';

void main() {
  test('root bucket values', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket bucket = RestorationBucket.root(manager: manager, rawData: rawData);

    expect(bucket.restorationId, 'root');
    expect(bucket.debugOwner, manager);

    // Bucket contains expected values from rawData.
    expect(bucket.read<int>('value1'), 10);
    expect(bucket.read<String>('value2'), 'Hello');
    expect(bucket.read<String>('value3'), isNull); // Does not exist.
    expect(manager.updateScheduled, isFalse);

    // Can overwrite existing value.
    bucket.write<int>('value1', 22);
    expect(manager.updateScheduled, isTrue);
    expect(bucket.read<int>('value1'), 22);
    manager.doSerialization();
    expect((rawData[valuesMapKey] as Map<String, dynamic>)['value1'], 22);
    expect(manager.updateScheduled, isFalse);

    // Can add a new value.
    bucket.write<bool>('value3', true);
    expect(manager.updateScheduled, isTrue);
    expect(bucket.read<bool>('value3'), true);
    manager.doSerialization();
    expect((rawData[valuesMapKey] as Map<String, dynamic>)['value3'], true);
    expect(manager.updateScheduled, isFalse);

    // Can remove existing value.
    expect(bucket.remove<int>('value1'), 22);
    expect(manager.updateScheduled, isTrue);
    expect(bucket.read<int>('value1'), isNull); // Does not exist anymore.
    manager.doSerialization();
    expect((rawData[valuesMapKey] as Map<String, dynamic>).containsKey('value1'), isFalse);
    expect(manager.updateScheduled, isFalse);

    // Removing non-existing value is no-op.
    expect(bucket.remove<Object>('value4'), isNull);
    expect(manager.updateScheduled, isFalse);

    // Can store null.
    bucket.write<bool?>('value4', null);
    expect(manager.updateScheduled, isTrue);
    expect(bucket.read<int>('value4'), null);
    manager.doSerialization();
    expect((rawData[valuesMapKey] as Map<String, dynamic>).containsKey('value4'), isTrue);
    expect((rawData[valuesMapKey] as Map<String, dynamic>)['value4'], null);
    expect(manager.updateScheduled, isFalse);
  });

  test('child bucket values', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rootRawData = _createRawDataSet();
    final Object debugOwner = Object();
    final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rootRawData);
    final RestorationBucket child = RestorationBucket.child(
      restorationId: 'child1',
      parent: root,
      debugOwner: debugOwner,
    );

    expect(child.restorationId, 'child1');
    expect(child.debugOwner, debugOwner);

    // Bucket contains expected values from rawData.
    expect(child.read<int>('foo'), 22);
    expect(child.read<String>('bar'), isNull); // Does not exist.
    expect(manager.updateScheduled, isFalse);

    // Can overwrite existing value.
    child.write<int>('foo', 44);
    expect(manager.updateScheduled, isTrue);
    expect(child.read<int>('foo'), 44);
    manager.doSerialization();
    expect((((rootRawData[childrenMapKey] as Map<String, dynamic>)['child1'] as Map<String, dynamic>)[valuesMapKey] as Map<String, dynamic>)['foo'], 44);
    expect(manager.updateScheduled, isFalse);

    // Can add a new value.
    child.write<bool>('value3', true);
    expect(manager.updateScheduled, isTrue);
    expect(child.read<bool>('value3'), true);
    manager.doSerialization();
    expect((((rootRawData[childrenMapKey] as Map<String, dynamic>)['child1'] as Map<String, dynamic>)[valuesMapKey] as Map<String, dynamic>)['value3'], true);
    expect(manager.updateScheduled, isFalse);

    // Can remove existing value.
    expect(child.remove<int>('foo'), 44);
    expect(manager.updateScheduled, isTrue);
    expect(child.read<int>('foo'), isNull); // Does not exist anymore.
    manager.doSerialization();
    expect(((rootRawData[childrenMapKey] as Map<String, dynamic>)['child1'] as Map<String, dynamic>).containsKey('foo'), isFalse);
    expect(manager.updateScheduled, isFalse);

    // Removing non-existing value is no-op.
    expect(child.remove<Object>('value4'), isNull);
    expect(manager.updateScheduled, isFalse);

    // Can store null.
    child.write<bool?>('value4', null);
    expect(manager.updateScheduled, isTrue);
    expect(child.read<int>('value4'), null);
    manager.doSerialization();
    expect((((rootRawData[childrenMapKey] as Map<String, dynamic>)['child1'] as Map<String, dynamic>)[valuesMapKey] as Map<String, dynamic>).containsKey('value4'), isTrue);
    expect((((rootRawData[childrenMapKey] as Map<String, dynamic>)['child1'] as Map<String, dynamic>)[valuesMapKey] as Map<String, dynamic>)['value4'], null);
    expect(manager.updateScheduled, isFalse);
  });

  test('claim child with existing data', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket bucket = RestorationBucket.root(manager: manager, rawData: rawData);

    final Object debugOwner = Object();
    final RestorationBucket child = bucket.claimChild('child1', debugOwner: debugOwner);

    expect(manager.updateScheduled, isFalse);
    expect(child.restorationId, 'child1');
    expect(child.debugOwner, debugOwner);

    expect(child.read<int>('foo'), 22);
    child.write('bar', 44);
    expect(manager.updateScheduled, isTrue);
    manager.doSerialization();
    expect((((rawData[childrenMapKey] as Map<String, dynamic>)['child1'] as Map<String, dynamic>)[valuesMapKey] as Map<String, dynamic>)['bar'], 44);
    expect(manager.updateScheduled, isFalse);
  });

  test('claim child with no existing data', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket bucket = RestorationBucket.root(manager: manager, rawData: rawData);

    expect((rawData[childrenMapKey] as Map<String, dynamic>).containsKey('child2'), isFalse);

    final Object debugOwner = Object();
    final RestorationBucket child = bucket.claimChild('child2', debugOwner: debugOwner);

    expect(manager.updateScheduled, isTrue);
    expect(child.restorationId, 'child2');
    expect(child.debugOwner, debugOwner);

    child.write('foo', 55);
    expect(child.read<int>('foo'), 55);
    manager.doSerialization();

    expect(manager.updateScheduled, isFalse);
    expect((rawData[childrenMapKey] as Map<String, dynamic>).containsKey('child2'), isTrue);
    expect((((rawData[childrenMapKey] as Map<String, dynamic>)['child2'] as Map<String, dynamic>)[valuesMapKey] as Map<Object?, Object?>)['foo'], 55);
  });

  test('claim child that is already claimed throws if not given up', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket bucket = RestorationBucket.root(manager: manager, rawData: rawData);

    final RestorationBucket child1 = bucket.claimChild('child1', debugOwner: 'FirstClaim');

    expect(manager.updateScheduled, isFalse);
    expect(child1.restorationId, 'child1');
    expect(child1.read<int>('foo'), 22);

    manager.doSerialization();
    expect(manager.updateScheduled, isFalse);

    final RestorationBucket child2 = bucket.claimChild('child1', debugOwner: 'SecondClaim');
    expect(child2.restorationId, 'child1');
    expect(child2.read<int>('foo'), isNull); // Value does not exist in this child.

    // child1 is not given up before running finalizers.
    expect(
      () => manager.doSerialization(),
      throwsA(isFlutterError.having(
        (FlutterError error) => error.message,
        'message',
        equals(
          'Multiple owners claimed child RestorationBuckets with the same IDs.\n'
          'The following IDs were claimed multiple times from the parent RestorationBucket(restorationId: root, owner: MockManager):\n'
          ' * "child1" was claimed by:\n'
          '   * SecondClaim\n'
          '   * FirstClaim (current owner)',
        ),
      )),
    );
  });

  test('claim child that is already claimed does not throw if given up', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket bucket = RestorationBucket.root(manager: manager, rawData: rawData);

    final RestorationBucket child1 = bucket.claimChild('child1', debugOwner: 'FirstClaim');

    expect(manager.updateScheduled, isFalse);
    expect(child1.restorationId, 'child1');
    expect(child1.read<int>('foo'), 22);

    manager.doSerialization();
    expect(manager.updateScheduled, isFalse);

    final RestorationBucket child2 = bucket.claimChild('child1', debugOwner: 'SecondClaim');
    expect(child2.restorationId, 'child1');
    expect(child2.read<int>('foo'), isNull); // Value does not exist in this child.
    child2.write<int>('bar', 55);

    // give up child1.
    child1.dispose();
    manager.doSerialization();
    expect(manager.updateScheduled, isFalse);
    expect((((rawData[childrenMapKey] as Map<String, dynamic>)['child1'] as Map<String, dynamic>)[valuesMapKey] as Map<Object?, Object?>).containsKey('foo'), isFalse);
    expect((((rawData[childrenMapKey] as Map<String, dynamic>)['child1'] as Map<String, dynamic>)[valuesMapKey] as Map<Object?, Object?>)['bar'], 55);
  });

  test('claiming a claimed child twice and only giving it up once throws', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket bucket = RestorationBucket.root(manager: manager, rawData: rawData);

    final RestorationBucket child1 = bucket.claimChild('child1', debugOwner: 'FirstClaim');
    expect(child1.restorationId, 'child1');
    final RestorationBucket child2 = bucket.claimChild('child1', debugOwner: 'SecondClaim');
    expect(child2.restorationId, 'child1');
    child1.dispose();
    final RestorationBucket child3 = bucket.claimChild('child1', debugOwner: 'ThirdClaim');
    expect(child3.restorationId, 'child1');
    expect(manager.updateScheduled, isTrue);
    expect(() => manager.doSerialization(), throwsFlutterError);
  });

  test('unclaiming and then claiming same id gives fresh bucket', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket bucket = RestorationBucket.root(manager: manager, rawData: rawData);

    final RestorationBucket child1 = bucket.claimChild('child1', debugOwner: 'FirstClaim');
    expect(manager.updateScheduled, isFalse);
    expect(child1.read<int>('foo'), 22);
    child1.dispose();
    expect(manager.updateScheduled, isTrue);
    final RestorationBucket child2 = bucket.claimChild('child1', debugOwner: 'SecondClaim');
    expect(child2.read<int>('foo'), isNull);
  });

  test('cleans up raw data if last value/child is dropped', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rawData);

    expect(rawData.containsKey(childrenMapKey), isTrue);
    final RestorationBucket child = root.claimChild('child1', debugOwner: 'owner');
    child.dispose();
    expect(manager.updateScheduled, isTrue);
    manager.doSerialization();
    expect(rawData.containsKey(childrenMapKey), isFalse);

    expect(rawData.containsKey(valuesMapKey), isTrue);
    expect(root.remove<int>('value1'), 10);
    expect(root.remove<String>('value2'), 'Hello');
    expect(manager.updateScheduled, isTrue);
    manager.doSerialization();
    expect(rawData.containsKey(valuesMapKey), isFalse);
  });

  test('dispose deletes data', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rawData);

    final RestorationBucket child1 = root.claimChild('child1', debugOwner: 'owner1');
    child1.claimChild('child1OfChild1', debugOwner: 'owner1.1');
    child1.claimChild('child2OfChild1', debugOwner: 'owner1.2');
    final RestorationBucket child2 = root.claimChild('child2', debugOwner: 'owner2');

    expect(manager.updateScheduled, isTrue);
    manager.doSerialization();
    expect(manager.updateScheduled, isFalse);

    expect((rawData[childrenMapKey] as Map<String, dynamic>).containsKey('child1'), isTrue);
    expect((rawData[childrenMapKey] as Map<String, dynamic>).containsKey('child2'), isTrue);

    child1.dispose();
    expect(manager.updateScheduled, isTrue);
    manager.doSerialization();
    expect(manager.updateScheduled, isFalse);

    expect((rawData[childrenMapKey] as Map<String, dynamic>).containsKey('child1'), isFalse);

    child2.dispose();
    expect(manager.updateScheduled, isTrue);
    manager.doSerialization();
    expect(manager.updateScheduled, isFalse);

    expect(rawData.containsKey(childrenMapKey), isFalse);
  });

  test('rename is no-op if same id', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rawData);

    final RestorationBucket child = root.claimChild('child1', debugOwner: 'owner1');

    expect(manager.updateScheduled, isFalse);
    expect(child.restorationId, 'child1');
    child.rename('child1');
    expect(manager.updateScheduled, isFalse);
    expect(child.restorationId, 'child1');
    expect((rawData[childrenMapKey] as Map<String, dynamic>).containsKey('child1'), isTrue);
  });

  test('rename to unused id', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rawData);

    final RestorationBucket child = root.claimChild('child1', debugOwner: 'owner1');
    final Object rawChildData = (rawData[childrenMapKey] as Map<String, dynamic>)['child1'] as Object;
    expect(rawChildData, isNotNull);

    expect(manager.updateScheduled, isFalse);
    expect(child.restorationId, 'child1');
    child.rename('new-name');
    expect(child.restorationId, 'new-name');

    expect(manager.updateScheduled, isTrue);
    manager.doSerialization();
    expect(manager.updateScheduled, isFalse);

    expect((rawData[childrenMapKey] as Map<Object?, Object?>).containsKey('child1'), isFalse);
    expect((rawData[childrenMapKey] as Map<Object?, Object?>)['new-name'], rawChildData);
  });

  test('rename to used id throws if id is not given up', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rawData);

    final RestorationBucket child1 = root.claimChild('child1', debugOwner: 'owner1');
    final RestorationBucket child2 = root.claimChild('child2', debugOwner: 'owner1');
    manager.doSerialization();

    expect(child1.restorationId, 'child1');
    expect(child2.restorationId, 'child2');
    child2.rename('child1');
    expect(child2.restorationId, 'child1');

    expect(manager.updateScheduled, isTrue);
    expect(() => manager.doSerialization(), throwsFlutterError);
  });

  test('rename to used id does not throw if id is given up', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rawData);

    final RestorationBucket child1 = root.claimChild('child1', debugOwner: 'owner1');
    final RestorationBucket child2 = root.claimChild('child2', debugOwner: 'owner1');
    manager.doSerialization();

    final Object rawChild1Data = (rawData[childrenMapKey] as Map<String, dynamic>)['child1'] as Object;
    expect(rawChild1Data, isNotNull);
    final Object rawChild2Data = (rawData[childrenMapKey] as Map<String, dynamic>)['child2'] as Object;
    expect(rawChild2Data, isNotNull);

    expect(child1.restorationId, 'child1');
    expect(child2.restorationId, 'child2');
    child2.rename('child1');
    expect(child2.restorationId, 'child1');
    expect(child1.restorationId, 'child1');

    child1.dispose();

    expect(manager.updateScheduled, isTrue);
    manager.doSerialization();
    expect(manager.updateScheduled, isFalse);

    expect((rawData[childrenMapKey] as Map<String, dynamic>)['child1'], rawChild2Data);
    expect((rawData[childrenMapKey] as Map<String, dynamic>).containsKey('child2'), isFalse);
  });

  test('renaming a to be added child', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rawData);

    final Object rawChild1Data = (rawData[childrenMapKey] as Map<String, dynamic>)['child1'] as Object;
    expect(rawChild1Data, isNotNull);

    final RestorationBucket child1 = root.claimChild('child1', debugOwner: 'owner1');
    final RestorationBucket child2 = root.claimChild('child1', debugOwner: 'owner1');

    child2.rename('foo');

    expect(manager.updateScheduled, isTrue);
    manager.doSerialization();
    expect(manager.updateScheduled, isFalse);

    expect(child1.restorationId, 'child1');
    expect(child2.restorationId, 'foo');

    expect((rawData[childrenMapKey] as Map<String, dynamic>)['child1'], rawChild1Data);
    expect((rawData[childrenMapKey] as Map<String, dynamic>)['foo'], isEmpty); // new bucket
  });

  test('adopt is no-op if same parent', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rawData);

    final RestorationBucket child1 = root.claimChild('child1', debugOwner: 'owner1');

    root.adoptChild(child1);
    expect(manager.updateScheduled, isFalse);
    expect((rawData[childrenMapKey] as Map<String, dynamic>).containsKey('child1'), isTrue);
  });

  test('adopt fresh child', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rawData);

    final RestorationBucket child = RestorationBucket.empty(restorationId: 'fresh-child', debugOwner: 'owner1');

    root.adoptChild(child);
    expect(manager.updateScheduled, isTrue);

    child.write('value', 22);

    manager.doSerialization();
    expect(manager.updateScheduled, isFalse);

    expect((rawData[childrenMapKey] as Map<String, dynamic>).containsKey('fresh-child'), isTrue);
    expect((((rawData[childrenMapKey] as Map<String, dynamic>)['fresh-child'] as Map<String, dynamic>)[valuesMapKey] as Map<Object?, Object?>)['value'], 22);

    child.write('bar', 'blabla');
    expect(manager.updateScheduled, isTrue);
  });

  test('adopt child that already had a parent', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rawData);

    final RestorationBucket child = root.claimChild('child1', debugOwner: 'owner1');
    final RestorationBucket childOfChild = child.claimChild('childOfChild', debugOwner: 'owner2');
    childOfChild.write<String>('foo', 'bar');

    expect(manager.updateScheduled, isTrue);
    manager.doSerialization();
    expect(manager.updateScheduled, isFalse);

    final Object childOfChildData = (((rawData[childrenMapKey] as Map<String, dynamic>)['child1'] as Map<String, dynamic>)[childrenMapKey] as Map<Object?, Object?>)['childOfChild']!;
    expect(childOfChildData, isNotEmpty);

    root.adoptChild(childOfChild);
    expect(manager.updateScheduled, isTrue);
    manager.doSerialization();
    expect(manager.updateScheduled, isFalse);

    expect(((rawData[childrenMapKey] as Map<String, dynamic>)['child1'] as Map<String, dynamic>).containsKey(childrenMapKey), isFalse); // child1 has no children anymore.
    expect((rawData[childrenMapKey] as Map<String, dynamic>)['childOfChild'], childOfChildData);
  });

  test('adopting child throws if id is already in use and not given up', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rawData);

    final RestorationBucket child = root.claimChild('child1', debugOwner: 'owner1');
    final RestorationBucket childOfChild = child.claimChild('child1', debugOwner: 'owner2');
    childOfChild.write<String>('foo', 'bar');

    root.adoptChild(childOfChild);
    expect(manager.updateScheduled, isTrue);
    expect(() => manager.doSerialization(), throwsFlutterError);
  });

  test('adopting child does not throw if id is already in use and given up', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rawData);

    final RestorationBucket child = root.claimChild('child1', debugOwner: 'owner1');
    final RestorationBucket childOfChild = child.claimChild('child1', debugOwner: 'owner2');
    childOfChild.write<String>('foo', 'bar');

    final Object childOfChildData = (((rawData[childrenMapKey] as Map<String, dynamic>)['child1'] as Map<String, dynamic>)[childrenMapKey] as Map<Object?, Object?>)['child1']!;
    expect(childOfChildData, isNotEmpty);

    expect(manager.updateScheduled, isTrue);
    manager.doSerialization();
    expect(manager.updateScheduled, isFalse);

    root.adoptChild(childOfChild);
    expect(manager.updateScheduled, isTrue);
    child.dispose();
    manager.doSerialization();
    expect(manager.updateScheduled, isFalse);

    expect((rawData[childrenMapKey] as Map<String, dynamic>)['child1'], childOfChildData);
  });

  test('adopting a to-be-added child under an already in use id', () {
    final MockRestorationManager manager = MockRestorationManager();
    final Map<String, dynamic> rawData = _createRawDataSet();
    final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rawData);

    final RestorationBucket child1 = root.claimChild('child1', debugOwner: 'owner1');
    final RestorationBucket child2 = root.claimChild('child2', debugOwner: 'owner1');

    expect(manager.updateScheduled, isTrue);
    manager.doSerialization();
    expect(manager.updateScheduled, isFalse);

    final RestorationBucket child1OfChild1 = child1.claimChild('child2', debugOwner: 'owner2');
    child1OfChild1.write<String>('hello', 'world');
    final RestorationBucket child2OfChild1 = child1.claimChild('child2', debugOwner: 'owner2');
    child2OfChild1.write<String>('foo', 'bar');

    root.adoptChild(child2OfChild1);
    child2.dispose();

    expect(manager.updateScheduled, isTrue);
    manager.doSerialization();
    expect(manager.updateScheduled, isFalse);

    expect((((rawData[childrenMapKey] as Map<String, dynamic>)['child2'] as Map<String, dynamic>)[valuesMapKey] as Map<Object?, Object?>)['foo'], 'bar');
    expect((((((rawData[childrenMapKey] as Map<String, dynamic>)
      ['child1'] as Map<String, dynamic>)
        [childrenMapKey] as Map<Object?, Object?>)
          ['child2']! as Map<String, dynamic>)
            [valuesMapKey] as Map<Object?, Object?>)
              ['hello'], 'world');
  });

  test('throws when used after dispose', () {
    final RestorationBucket bucket = RestorationBucket.empty(restorationId: 'foo', debugOwner: null);
    bucket.dispose();

    expect(() => bucket.debugOwner, throwsFlutterError);
    expect(() => bucket.restorationId, throwsFlutterError);
    expect(() => bucket.read<int>('foo'), throwsFlutterError);
    expect(() => bucket.write('foo', 10), throwsFlutterError);
    expect(() => bucket.remove<int>('foo'), throwsFlutterError);
    expect(() => bucket.contains('foo'), throwsFlutterError);
    expect(() => bucket.claimChild('child', debugOwner: null), throwsFlutterError);
    final RestorationBucket child = RestorationBucket.empty(restorationId: 'child', debugOwner: null);
    expect(() => bucket.adoptChild(child), throwsFlutterError);
    expect(() => bucket.rename('bar'), throwsFlutterError);
    expect(() => bucket.dispose(), throwsFlutterError);
  });
}

Map<String, dynamic> _createRawDataSet() {
  return <String, dynamic>{
    valuesMapKey: <String, dynamic>{
      'value1' : 10,
      'value2' : 'Hello',
    },
    childrenMapKey: <String, dynamic>{
      'child1' : <String, dynamic>{
        valuesMapKey : <String, dynamic>{
          'foo': 22,
        },
      },
    },
  };
}
