Add test for #47852

Bug: #47852
Change-Id: If17635dfcf9794edc2cf23ad5219ed87fda41fcb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/393182
Reviewed-by: Lasse Nielsen <lrn@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
diff --git a/tests/lib/collection/regress_47852_test.dart b/tests/lib/collection/regress_47852_test.dart
new file mode 100644
index 0000000..4345878
--- /dev/null
+++ b/tests/lib/collection/regress_47852_test.dart
@@ -0,0 +1,99 @@
+// Copyright (c) 2024, 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 'dart:collection';
+
+import 'package:expect/expect.dart';
+
+/// Bug in dev-compiler's [putIfAbsent](https://dartbug.com/47852).
+void originalError() {
+  final map = {};
+  final key = DateTime.now(); // Overrides Object.==/hashCode
+  bool wasAbsent = false;
+  map.putIfAbsent(key, () {
+    wasAbsent = true;
+    Expect.isFalse(
+        map.containsKey(key), 'containsKey should be false in putIfAbsent');
+    return key;
+  });
+  Expect.isTrue(wasAbsent);
+}
+
+enum AnEnum { element1, element2 }
+
+class Dumb {
+  final Object field;
+  Dumb(this.field);
+
+  // Dumb hashCode to stress same-bucket paths.
+  int get hashCode => 0;
+  bool operator ==(Object other) => other is Dumb && this.field == other.field;
+
+  String toString() => 'Dumb($field)';
+}
+
+// Test keys. These instances of classes that do and don't override `==` and
+// `hashCode`, and a variety of primitive values since we generally don't know
+// whether they have overrides.
+final keys = [
+  123,
+  3.14,
+  10.0,  // int or double depending on platform.
+  'aString',
+  #someSymbol,
+  true,
+  false,
+  null,
+  AnEnum.element1,
+  AnEnum.element2,
+  DateTime.now(),
+  Dumb(1),
+  Dumb(2),
+  Dumb(3),
+  Object(),
+  const [1],
+  const {1},
+  const {'x'},
+  // const maps have different implementation types depending on keys.
+  const {'x': 1},
+  const {123: 1},
+  const {AnEnum.element1: 1, #foo: 2},
+];
+
+void testMap(Map<Object?, Object?> map) {
+  for (final key in keys) {
+    bool wasAbsent = false;
+    map.putIfAbsent(key, () {
+      wasAbsent = true;
+      Expect.isFalse(map.containsKey(key),
+          'containsKey should be false in putIfAbsent. key = $key');
+      return key;
+    });
+    Expect.isTrue(wasAbsent);
+    Expect.isTrue(map.containsKey(key), 'Key was not added. key = $key');
+  }
+}
+
+void main() {
+  originalError();
+
+  testMap({});
+  testMap(Map()); // Should be same as `{}`.
+  testMap(HashMap());
+  testMap(LinkedHashMap()); // Should be same as `{}`.
+
+  testMap(Map.identity());
+  testMap(HashMap.identity());
+  testMap(LinkedHashMap.identity()); // Should be same as `Map.identity()`.
+
+  // Custom maps:
+  testMap(HashMap(
+      equals: (k1, k2) => k2 == k1,
+      hashCode: (key) => key.hashCode + 1,
+      isValidKey: (_) => true));
+  testMap(LinkedHashMap(
+      equals: (k1, k2) => k2 == k1,
+      hashCode: (key) => key.hashCode + 1,
+      isValidKey: (_) => true));
+}