Merge pull request #108 from google/google

Integrate internal changes
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index 0994653..0000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-# This workflow uses actions that are not certified by GitHub.
-# They are provided by a third-party and are governed by
-# separate terms of service, privacy policy, and support
-# documentation.
-
-name: ci
-
-on:
-  push:
-    branches: [ master ]
-  pull_request:
-    branches: [ master ]
-
-jobs:
-  analyze:
-    runs-on: ubuntu-latest
-
-    steps:
-      - uses: actions/checkout@v2
-      - uses: dart-lang/setup-dart@v1.0
-        with:
-          sdk: dev
-      - run: pub get
-      - run: dart format --output=none --set-exit-if-changed .
-      - run: dart analyze --fatal-infos
-  test:
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        sdk: [2.12.0, stable, dev]
-    steps:
-      - uses: actions/checkout@v2
-      - uses: dart-lang/setup-dart@v1.0
-        with:
-          sdk: ${{ matrix.sdk }}
-      - run: pub get
-      - run: pub run test -p vm,chrome
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index e43b40f..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,9 +0,0 @@
-# Files and directories created by pub
-.dart_tool/
-.packages
-.pub/
-packages
-pubspec.lock
-
-# Directory created by dartdoc
-doc/api/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4ec2494..f1c8c44 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
 ## 0.24.0-dev
 
 * Migrate to [null safety](https://dart.dev/null-safety).
+* Update ObservableMap to notify observers about changes for all methods.
 
 ## 0.23.0
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 6f5e0ea..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,33 +0,0 @@
-Want to contribute? Great! First, read this page (including the small print at
-the end).
-
-### Before you contribute
-Before we can use your code, you must sign the
-[Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual)
-(CLA), which you can do online. The CLA is necessary mainly because you own the
-copyright to your changes, even after your contribution becomes part of our
-codebase, so we need your permission to use and distribute your code. We also
-need to be sure of various other things—for instance that you'll tell us if you
-know that your code infringes on other people's patents. You don't have to sign
-the CLA until after you've submitted your code for review and a member has
-approved it, but you must do it before we can put your code into our codebase.
-
-Before you start working on a larger contribution, you should get in touch with
-us first through the issue tracker with your idea so that we can help out and
-possibly guide you. Coordinating up front makes it much easier to avoid
-frustration later on.
-
-### Code reviews
-All submissions, including submissions by project members, require review.
-
-### File headers
-All files in the project must start with the following header.
-
-    // 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.
-
-### The small print
-Contributions made by corporations are covered by a different agreement than the
-one above, the
-[Software Grant and Corporate Contributor License Agreement](https://developers.google.com/open-source/cla/corporate).
diff --git a/lib/src/observable_list.dart b/lib/src/observable_list.dart
index c25534f..85c34ef 100644
--- a/lib/src/observable_list.dart
+++ b/lib/src/observable_list.dart
@@ -244,7 +244,6 @@
     // We are modifying the length just below these checks. Without the checks
     // Array.copy could throw an exception, leaving the list in a bad state
     // (with a length that has been increased, but without a new element).
-    if (index is! int) throw ArgumentError(index);
     RangeError.checkValidIndex(index, this);
     _list
       // Increase the length by adding [element], in case [E] isn't nullable.
diff --git a/lib/src/observable_map.dart b/lib/src/observable_map.dart
index ec256f7..05f2a4f 100644
--- a/lib/src/observable_map.dart
+++ b/lib/src/observable_map.dart
@@ -190,7 +190,9 @@
 
   @override
   void addEntries(Iterable<MapEntry<K, V>> entries) {
-    _map.addEntries(entries);
+    for (var entry in entries) {
+      this[entry.key] = entry.value;
+    }
   }
 
   @override
@@ -200,15 +202,21 @@
 
   @override
   V update(K key, V Function(V value) update, {V Function()? ifAbsent}) {
-    return _map.update(key, update, ifAbsent: ifAbsent);
+    var value = containsKey(key) ? update(this[key] as V) : ifAbsent!();
+    return this[key] = value;
   }
 
   @override
-  void updateAll(V Function(K key, V value) update) => _map.updateAll(update);
+  void updateAll(V Function(K key, V value) update) {
+    for (var key in keys) {
+      this[key] = update(key, this[key] as V);
+    }
+  }
 
   @override
-  void removeWhere(bool Function(K key, V value) test) =>
-      _map.removeWhere(test);
+  void removeWhere(bool Function(K key, V value) test) {
+    keys.where((key) => test(key, this[key] as V)).toList().forEach(remove);
+  }
 
   // Note: we don't really have a reasonable old/new value to use here.
   // But this should fix "keys" and "values" in templates with minimal overhead.
diff --git a/test/change_notifier_test.dart b/test/change_notifier_test.dart
index b412742..a6d0385 100644
--- a/test/change_notifier_test.dart
+++ b/test/change_notifier_test.dart
@@ -56,7 +56,10 @@
     test(
         'delivers expectChangesed changes',
         () => runTest<B>((cn) {
-              cn..notifyChange(B(1))..notifyChange(B(2))..notifyChange(B(3));
+              cn
+                ..notifyChange(B(1))
+                ..notifyChange(B(2))
+                ..notifyChange(B(3));
             }, (cr) {
               expectChanges(cr, ChangeRecords<B>.wrap([B(1), B(2), B(3)]));
             }));
diff --git a/test/observable_map_test.dart b/test/observable_map_test.dart
index 5743001..b980983 100644
--- a/test/observable_map_test.dart
+++ b/test/observable_map_test.dart
@@ -86,7 +86,7 @@
   });
 
   group('observe item', () {
-    late ObservableMap map;
+    late ObservableMap<String, int?> map;
     List<ChangeRecord>? changes;
 
     setUp(() {
@@ -177,6 +177,48 @@
         ]);
       });
     });
+
+    test('change the item as part of addAll', () {
+      map.addAll({'b': 13, 'd': 4});
+      expect(map, {'a': 1, 'b': 13, 'c': 3, 'd': 4});
+      return Future(() {
+        expect(changes, [_changeKey('b', 2, 13)]);
+      });
+    });
+
+    test('change the item as part of addEntries', () {
+      map.addEntries(
+          [MapEntry<String, int>('b', 13), MapEntry<String, int>('d', 4)]);
+      expect(map, {'a': 1, 'b': 13, 'c': 3, 'd': 4});
+      return Future(() {
+        expect(changes, [_changeKey('b', 2, 13)]);
+      });
+    });
+
+    test('update the item', () {
+      map.update('b', (int? value) => value == null ? value : value + 1);
+      expect(map, {'a': 1, 'b': 3, 'c': 3});
+      return Future(() {
+        expect(changes, [_changeKey('b', 2, 3)]);
+      });
+    });
+
+    test('update all items', () {
+      map.updateAll(
+          (String key, int? value) => value == null ? value : value + 1);
+      expect(map, {'a': 2, 'b': 3, 'c': 4});
+      return Future(() {
+        expect(changes, [_changeKey('b', 2, 3)]);
+      });
+    });
+
+    test('remove the item as part of removeWhere', () {
+      map.removeWhere((key, value) => value != null && value > 1);
+      expect(map, {'a': 1});
+      return Future(() {
+        expect(changes, [_removeKey('b', 2)]);
+      });
+    });
   });
 
   test('toString', () {